Changes between Version 23 and Version 24 of P9


Ignore:
Timestamp:
06/23/26 00:10:08 (3 days ago)
Author:
211099
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • P9

    v23 v24  
    864864    throw new UnauthorizedAccessException("Invalid email or password.");
    865865}}}
     866
     867== SQL Injection Prevention
     868To reduce the risk of SQL injection, ChapterX uses Entity Framework Core as its ORM instead of writing raw SQL queries. EF Core automatically generates parameterized queries under the hood, user-supplied values are never concatenated directly into query strings.
     869
     870Because all database access goes through EF Core's API, parameterization is handled implicitly:
     871{{{
     872public async Task<T?> GetByIdAsync(int id, CancellationToken cancellationToken = default)
     873    => await _dbSet.FindAsync([id], cancellationToken);
     874
     875public async Task AddAsync(T entity, CancellationToken cancellationToken = default)
     876{
     877    await _dbSet.AddAsync(entity, cancellationToken);
     878    await _context.SaveChangesAsync(cancellationToken);
     879}
     880}}}
     881Custom queries in repositories follow the same pattern — LINQ expressions are translated by EF Core into parameterized SQL, so user input is always treated as a value, never as executable SQL:
     882{{{
     883public async Task<User?> GetByEmailAsync(string email, CancellationToken cancellationToken = default)
     884    => await _dbSet.FirstOrDefaultAsync(u => u.Email == email, cancellationToken);
     885public async Task<IEnumerable<Chapter>> GetByStoryIdAsync(int storyId, CancellationToken cancellationToken = default)
     886    => await _dbSet.Where(c => c.StoryId == storyId).ToListAsync(cancellationToken);
     887}}}