WEBSITE ĐANG PHÁT TRIỂN

API security checklist 2026 - những lỗ hổng developer cần biết

Năm 2026, 97% lỗ hổng API có thể bị khai thác chỉ bằng một request. BOLA (Broken Object Level Authorization) vẫn là vua, nhưng authentication bypass và "zombie APIs" mang lại rủi ro mới. Team chúng tôi tổng hợp checklist hành động bắt buộc cho mọi backend developer.

Vấn đề: Khi API trở thành cánh cửa mở

Tháng 9 năm 2025, một fintech startup ở HCM mất gần 5 tỷ đồng trong vòng 2 giờ. Không phải qua SQL injection hay ransomware-mà qua một API endpoint mà team quên yêu cầu authentication.

Endpoint đó gốc là GET /api/v1/health-check-dùng để kiểm tra máy chủ có sống không. Nhưng kỹ sư cũ để lọt logic transfer tiền vào file đó. Không ai ngờ. Attacker tìm được thông qua GitHub reconnaissance, gửi hàng triệu request, ăn cắp dữ liệu tài khoản. Xong.

Đó không phải trường hợp lẻ. Theo báo cáo 42Crunch 2026, 97% API vulnerabilities có thể bị khai thác với một request duy nhất. Gần 99% có thể access từ xa. Còn 59% không cần authentication gì hết.


Concept: OWASP API Top 10 2026 - Những gì chúng tôi phải chống lại

Chúng tôi không thể bảo vệ cái mình không hiểu. Vì vậy mở đầu từ top vulnerabilities:

1. Broken Object Level Authorization (BOLA / IDOR)

Khi bạn cho phép user truy cập vào object của user khác qua direct reference mà không validate quyền.

Ví dụ thực tế:

// ❌ VULNERABLE - Attacker đổi user_id trong URL
[HttpGet("/api/users/{userId}/profile")]
public ActionResult<UserProfile> GetUserProfile(int userId)
{
    var profile = _db.Users.FirstOrDefault(u => u.Id == userId);
    return Ok(profile); // Không kiểm tra current user có quyền xem không
}

// ✓ SECURE - Validate authorization trước
[HttpGet("/api/users/{userId}/profile")]
[Authorize]
public ActionResult<UserProfile> GetUserProfile(int userId)
{
    var currentUser = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;

    // Chỉ user chính hoặc admin mới được xem
    if (currentUser != userId.ToString() && !User.IsInRole("Admin"))
    {
        return Forbid("You don't have permission to access this profile");
    }

    var profile = _db.Users.FirstOrDefault(u => u.Id == userId);
    return Ok(profile);
}

2. Broken Authentication

Không phải mỗi request đều có authentication token. Nhiều team để cho API endpoints hoạt động mà không kiểm tra:

  • Health checks mà chứa logic thực
  • Internal endpoints (kiểu như admin panel) nhưng exposed
  • Legacy API versions vẫn chạy với old auth scheme (Basic Auth)
  • Implicit grant flows cho OAuth (không có PKCE)

Thực tế 2026: Một fintech châu Á để lại endpoint /api/internal/calculate-interest-rate mà quên thêm [Authorize]. Attacker đổi tỷ lệ lãi suất trong database qua endpoint đó, gây lỗ hổng trên hàng chục khoản vay.

3. Unrestricted Resource Consumption (DoS)

Không có rate limit hoặc resource limit. Attacker spam API hàng triệu request, làm server crash hoặc tăng bill cloud.

4. Security Misconfiguration

Để debug mode bật ở production, return error messages chi tiết, không set CORS đúng, không validate HTTPS.


Checklist Hành Động: 10 Bước Bắt Buộc

Chúng tôi đã xem qua hàng trăm API audit. Đây là checklist mà team chúng tôi dùng cho mỗi API endpoint:

1. Authentication on Every Endpoint (Không ngoại lệ)

[ApiController]
[Route("api/[controller]")]
[Authorize] // ← Bắt buộc. Ngoại lệ phải có [AllowAnonymous]
public class OrdersController : ControllerBase
{
    [HttpGet("{id}")]
    public ActionResult<Order> GetOrder(int id)
    {
        return Ok(_db.Orders.Find(id));
    }

    [HttpPost]
    [Authorize(Roles = "Admin")] // ← Role-based access khi cần
    public ActionResult CreateOrder([FromBody] CreateOrderRequest req)
    {
        // ...
    }

    [HttpGet("health")]
    [AllowAnonymous] // ← Ngoại lệ rõ ràng & không chứa logic thực
    public ActionResult Health()
    {
        return Ok("OK");
    }
}

Checklist:

  • [ ] Mỗi endpoint có [Authorize] hoặc rõ ràng explain tại sao [AllowAnonymous]
  • [ ] Ngoại lệ (health checks) không chứa bất kỳ business logic nào
  • [ ] JWT token hoặc session được validate chặt chẽ

2. Implement Object Level Authorization

[HttpGet("orders/{orderId}")]
[Authorize]
public ActionResult<Order> GetOrder(int orderId)
{
    var currentUserId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;

    var order = _db.Orders
        .Include(o => o.Customer)
        .FirstOrDefault(o => o.Id == orderId);

    if (order == null) return NotFound();

    // Kiểm tra: user này sở hữu order này không?
    if (order.CustomerId.ToString() != currentUserId && !User.IsInRole("Admin"))
    {
        return Forbid();
    }

    return Ok(order);
}

Checklist:

  • [ ] Mỗi query lấy data từ DB đều lọc theo current user (trừ Admin)
  • [ ] Kiểm tra resource ownership trước khi return data
  • [ ] Test case: verify user A không thể xem order của user B

3. Disable Debug Mode & Hide Version Headers

// Program.cs
var builder = WebApplication.CreateBuilder(args);

// Chỉ enable detail errors ở development
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/api/error");
    app.UseHsts();

    // Ẩn Server header (reveal framework version)
    app.Use(async (context, next) =>
    {
        context.Response.Headers.Remove("Server");
        context.Response.Headers.Remove("X-Powered-By");
        context.Response.Headers.Remove("X-AspNet-Version");
        await next();
    });
}

app.UseHttpsRedirection();
app.UseCors();

Checklist:

  • [ ] clean
  • [ ] Error pages không leak stack traces ở production
  • [ ] Debug symbols stripped from production binary

4. Implement Rate Limiting & Throttling

// Thêm vào Program.cs
builder.Services.AddRateLimiter(options =>
{
    options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;

    // Global limit: 100 requests per minute
    options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
        RateLimitPartition.GetFixedWindowLimiter(
            partitionKey: httpContext.User.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? httpContext.Connection.RemoteIpAddress?.ToString() ?? "anonymous",
            factory: partition => new FixedWindowRateLimiterOptions
            {
                PermitLimit = 100,
                Window = TimeSpan.FromMinutes(1)
            }));
});

app.UseRateLimiter();

// Hoặc per-endpoint
[HttpGet("sensitive-operation")]
[Authorize]
[RequireRateLimiting("strict")] // ← 10 req/minute
public ActionResult SensitiveOp()
{
    return Ok();
}

Checklist:

  • [ ] Rate limit set trên tất cả public endpoints
  • [ ] Sensitive operations (transfer, delete) có limit chặt hơn
  • [ ] Monitor rate limit violations via logging

5. Validate Input Strictly (Allowlist, Not Denylist)

[HttpPost("orders")]
public ActionResult CreateOrder([FromBody] CreateOrderRequest req)
{
    // ❌ Denylist (yếu)
    // if (!req.Description.Contains("DROP TABLE")) ...

    // ✓ Allowlist (mạnh)
    var validation = new OrderValidator();
    var result = validation.Validate(req);
    if (!result.IsValid) return BadRequest(result.Errors);

    // Parameterized query (EF Core handles this)
    _db.Orders.Add(new Order
    {
        CustomerId = int.Parse(req.CustomerId), // Validate integer
        Amount = decimal.Parse(req.Amount),
        Description = req.Description[..100] // Truncate to max length
    });
    _db.SaveChanges();

    return CreatedAtAction(nameof(GetOrder), new { id = order.Id });
}

FluentValidation example:

public class CreateOrderRequestValidator : AbstractValidator<CreateOrderRequest>
{
    public CreateOrderRequestValidator()
    {
        RuleFor(x => x.CustomerId)
            .NotEmpty()
            .Matches(@"^\d+$"); // Only digits

        RuleFor(x => x.Amount)
            .GreaterThan(0)
            .LessThanOrEqualTo(1000000);

        RuleFor(x => x.Description)
            .MaximumLength(500)
            .Matches(@"^[a-zA-Z0-9\s\-.,]+$"); // Whitelist characters
    }
}

Checklist:

  • [ ] Input validation library (FluentValidation, DataAnnotations)
  • [ ] Allowlist regex patterns, not blacklist
  • [ ] Database queries use parameterized queries (EF Core default)

6. Use HTTPS Everywhere & Set Security Headers

// Program.cs
app.UseHttpsRedirection();
app.UseHsts(); // Add 'Strict-Transport-Security' header

app.Use(async (context, next) =>
{
    context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
    context.Response.Headers.Add("X-Frame-Options", "DENY");
    context.Response.Headers.Add("X-XSS-Protection", "1; mode=block");
    context.Response.Headers.Add("Content-Security-Policy", "default-src 'self'");
    await next();
});

Checklist:

  • [ ] All HTTP redirects to HTTPS
  • [ ] HSTS enabled (preload list recommended)
  • [ ] CORS policy strict (không Access-Control-Allow-Origin: *)
  • [ ] CSP header set

7. Audit & Log API Calls (Không log sensitive data)

app.Use(async (context, next) =>
{
    var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? "anonymous";
    var path = context.Request.Path;
    var method = context.Request.Method;

    var sw = System.Diagnostics.Stopwatch.StartNew();
    await next();
    sw.Stop();

    // Log (nhưng không log body nếu chứa passwords, tokens)
    _logger.LogInformation(
        "API Call: {Method} {Path} | User: {UserId} | Status: {StatusCode} | Duration: {Duration}ms",
        method, path, userId, context.Response.StatusCode, sw.ElapsedMilliseconds
    );

    // Alert if suspicious
    if (context.Response.StatusCode >= 400 && context.Response.StatusCode != 404)
    {
        _logger.LogWarning("Failed API call: {Method} {Path} returned {Status}", method, path, context.Response.StatusCode);
    }
});

Checklist:

  • [ ] Logging middleware records auth failures
  • [ ] No passwords, tokens, or PII in logs
  • [ ] Centralized logging (ELK, Application Insights)

8. Manage API Versions Carefully

// ✓ Version endpoint explicitly
[ApiVersion("1.0")]
[ApiVersion("2.0")]
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
public class OrdersController : ControllerBase
{
    [HttpGet("{id}"), MapToApiVersion("1.0")]
    public ActionResult<OrderV1> GetOrderV1(int id) { /* legacy */ }

    [HttpGet("{id}"), MapToApiVersion("2.0")]
    public ActionResult<OrderV2> GetOrderV2(int id) { /* new */ }
}

// ❌ Don't leave old versions running indefinitely
// They often have older dependencies with CVEs

Checklist:

  • [ ] API versioning strategy documented
  • [ ] Deprecate old versions (e.g., v1 sunset date: 2026-12-31)
  • [ ] Monitor traffic on deprecated versions
  • [ ] Audit old versions for outdated libraries

9. Protect Against SSRF & Open Redirects

[HttpGet("fetch-external-data")]
[Authorize]
public async Task<ActionResult> FetchExternalData([FromQuery] string url)
{
    // ❌ Vulnerable to SSRF
    // var data = await _httpClient.GetStringAsync(url);

    // ✓ Whitelist allowed domains
    var allowedDomains = new[]
    {
        "https://api.trusted-partner.com",
        "https://data.trusted-source.io"
    };

    var uri = new Uri(url);
    if (!allowedDomains.Any(d => uri.Host == new Uri(d).Host))
    {
        return BadRequest("URL not in whitelist");
    }

    // Prevent redirect to malicious site
    var handler = new HttpClientHandler
    {
        AllowAutoRedirect = false // Explicit control
    };

    using (var client = new HttpClient(handler))
    {
        var response = await client.GetAsync(url);
        if (response.StatusCode == System.Net.HttpStatusCode.Redirect)
        {
            var location = response.Headers.Location?.ToString();
            // Verify redirect target is safe
        }
    }

    return Ok(await response.Content.ReadAsStringAsync());
}

Checklist:

  • [ ] External URL requests whitelisted
  • [ ] Redirect targets validated
  • [ ] HTTP client library patched to latest version

10. Dependency & Supply Chain Security

// Program.cs
// Package.csproj - update regularly
// ❌ Old: <PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
// ✓ New: <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />

// Monitor dependencies
// dotnet list package --outdated
// dotnet list package --vulnerable

Checklist:

  • [ ] dotnet list package --vulnerable runs clean in CI/CD
  • [ ] Dependency update policy (e.g., monthly security patches)
  • [ ] NuGet package signature verification (nuget.config)
  • [ ] SCA tool (Snyk, Dependabot, GitHub Advanced Security)

So Sánh: Good vs Bad API

Tiêu chí ❌ Bad ✓ Good
Auth Endpoints không [Authorize] Mỗi endpoint rõ ràng authorize
Object Access User A xem dữ liệu User B qua ID Object ownership validated
Rate Limit Không limit 100 req/min/user, 10 req/min sensitive ops
Errors Return stack trace ở prod Generic error, detailed logs internally
HTTPS HTTP allowed HTTPS only + HSTS
Dependencies 2+ năm không update Monthly security audit, patch trong 48h
Versions 5 versions running, no sunset Explicit versioning, deprecation timeline
Logging Log passwords Log user_id, timestamp, action (no secrets)

Kết Luận & CTA

API là trái tim của hầu hết ứng dụng hiện đại. Nhưng theo báo cáo 2026, 97% API vulnerabilities vẫn khá dễ khai thác. Điều đó có nghĩa: basic security hygiene tiết kiệm hàng tỷ.

Team chúng tôi recommend:

  1. Ngay hôm nay: Chạy dotnet list package --vulnerable trên codebase. Fix ngay.
  2. Tuần này: Audit 3-5 critical endpoints bằng checklist trên.
  3. Tháng này: Implement rate limiting + security header middleware.
  4. Q2 2026: SCA tool (Snyk hoặc Dependabot) vào CI/CD.

Chúng tôi không nói bảo mật API khó. Nó chỉ cần sự kiên trì. Một endpoint quên [Authorize] có thể cost hàng tỷ. Checklist này giúp team chúng tôi, và giờ giúp bạn.

Bạn đang làm gì với API security ngay bây giờ? Feedback ở dưới comment. Chúng tôi luôn lắng nghe.


Tham Khảo


BKGlobal Tech Team

Cập nhật: 2026-03-19


Bài viết liên quan

Xem thêm
Tool & Platform mới cho Developer

Trae IDE: ByteDance's Free AI IDE — Đối thử miễn phí của Cursor

Trae IDE là AI IDE hoàn toàn miễn phí từ ByteDance, công ty đứng sau TikTok. Built trên VS Code, tích hợp Claude 4.5 Sonnet, GPT-5o, và mới nhất là Grok. Miễn phí hoàn toàn — tất cả AI features, không có paywall. Nhưng có một "nhưng" lớn về privacy.

Tool & Platform mới cho Developer

OpenCode: Open-source Terminal Agent 95K Stars — Provider-agnostic AI Coding

OpenCode là open-source terminal agent phổ biến nhất trên GitHub với 95K stars, hỗ trợ 75+ AI models từ nhiều providers. Miễn phí với built-in free models, hoặc BYOK với API key bạn chọn. Đây là lowest-friction entry point cho developers muốn thử terminal agents.

Tool & Platform mới cho Developer

OpenAI Codex CLI: Terminal Agent quay lại cuộc chơi 2026

OpenAI Codex CLI re-entered cuộc trò chuyện đầu 2026 với parallel sandboxed execution và automatic PR creation. 3% adoption (trước khi desktop app launch), nhưng đang tăng. Strong choice nếu bạn đã ở trong OpenAI ecosystem và muốn terminal-first AI agent.