← 返回蜂巢洞察

如何向英国税务海关总署的“数字化纳税”API提交季度更新报告

每年有四次,所有参与“税收数字化”计划的个体经营者和房东都必须向英国税务海关总署提交一份收入与支出的汇总报表。 2026-27纳税年度的第一个截止日期是8月7日,这个期限已经过去;第二个截止日期则是11月7日。每份汇总报表都需要通过软件向英国税务海关总署发送一次API请求,而本教程正是专门讲解如何正确完成这些请求操作的。 您无需事先阅读任何其他资料即可跟随本教程进行操作。每次进行“税收数字化”集成时所需的一次性设置信息(包括沙箱应用程序、OAuth 2.0访问令牌以及防欺诈相关配置)已在下方的简要总结中列出,同时每段代码示例中也都会使用到一些辅助函数。 如果您想深入了解这些设置步骤,我曾在 之

每年有四次,所有参与“税收数字化”计划的个体经营者和房东都必须向英国税务海关总署提交一份收入与支出的汇总报表。

2026-27纳税年度的第一个截止日期是8月7日,这个期限已经过去;第二个截止日期则是11月7日。每份汇总报表都需要通过软件向英国税务海关总署发送一次API请求,而本教程正是专门讲解如何正确完成这些请求操作的。

您无需事先阅读任何其他资料即可跟随本教程进行操作。每次进行“税收数字化”集成时所需的一次性设置信息(包括沙箱应用程序、OAuth 2.0访问令牌以及防欺诈相关配置)已在下方的简要总结中列出,同时每段代码示例中也都会使用到一些辅助函数。

如果您想深入了解这些设置步骤,我曾在之前的freeCodeCamp教程中逐步进行了讲解。

完成本教程后,您将能够掌握如何查找自己需要申报的企业信息、确定应缴纳税款的期限、生成英国税务海关总署认可的累计汇总报表并提交它,同时还能了解相关的税款计算流程。所使用的代码是用Node.js和TypeScript编写的,这些代码是我为某个“税收数字化”应用程序开发的。

目录

快速回顾:本教程所假设的设置条件

在开始提交任何申报资料之前,您的应用程序必须完成与所有“税收数字化”集成项目相同的一次性设置。如果您已经完成了这些设置,可以直接跳到下一节;否则,请参考以下简要说明:

  1. 在英国税务海关总署开发者中心注册一个沙箱应用程序,并订阅这里所使用的四个API接口:企业信息、纳税义务、个体经营者相关数据以及个人税款计算相关服务。如果您没有订阅某个API接口,系统会返回403 Forbidden错误代码,这看似是权限问题,但实际上并非如此。

  2. 使用英国税务海关总署提供的创建测试用户API功能,创建一个虚拟的纳税人账户。该账户会拥有国家保险号码和政府门户系统的登录凭证。

  3. 通过英国税务海关总署的OAuth 2.0授权流程获取访问令牌。在申请令牌时,需要选择read:self-assessment和write:self-assessment权限范围,并以测试用户的身份在授权页面上完成登录操作。

  4. 在每次发送请求时都必须添加防欺诈相关头部信息。英国税务海关总署明确规定必须这样做,具体需要添加哪些头部信息取决于您的应用程序的连接方式,因此请在getFraudHeaders(req)函数中统一生成这些信息。

本教程中的每个代码片段都会使用一个小的辅助函数。这些函数用于设置承载令牌,通过Accept头部来指定API版本(HMRC的每个API都有其对应的版本号,如果使用错误的版本号,系统会返回406 Not Acceptable错误响应),同时还会添加用于防止欺诈的头部信息:

import axios from 'axios';

const HMRC_BASE_URL = 'https://test-api.service.hmrc.gov.uk'; // 沙盒环境

async function request(method, path, accessToken, req, data = null, apiVersion = '2.0') {
  const headers = {
    Authorization: 'Bearer ' + accessToken,
    Accept: 'application/vnd.hmrc.' + apiVersion + '+json',
    ...getFraudHeaders(req),
  };
  // 只有在请求体存在的情况下,才需要设置JSON Content-Type。否则HMRC的边缘服务器会拒绝接收没有请求体的GET请求,并返回403错误。
  if (data !== null && data !== undefined) {
    headers['Content-Type'] = 'application/json';
  }
  const res = await axios({ baseURL: HMRC_BASE_URL, method, url: path, headers, data });
  return res.data;
}

req参数代表用户的请求信息(在我的代码中,这是Express框架中的Request对象),欺诈预防头部信息中的客户端详细数据就是从这里获取的。只要有了承载令牌以及这个辅助函数,你就可以开始提交相关文件了。

MTD模式中季度更新的具体运作方式

季度更新并不等同于纳税申报表。它实际上是一组累计数据,反映了企业在当前纳税年度内迄今为止所赚取的收入和支出的金额,这些分类与自我评估系统已经使用的分类是一致的。

关键在于“迄今为止”这个时间点。每次更新都是累积性的,它涵盖的是从纳税年度开始到当前更新周期结束期间的所有数据,而不仅仅是最近三个月的信息。英国政府关于提交季度更新的指南中明确规定了各更新周期的标准时间范围及其截止日期:

更新周期 截止日期
4月6日至7月5日 8月7日
4月6日至10月5日 11月7日
4月6日至1月5日 2月7日
4月6日至4月5日 次年5月7日
2026-27纳税年度的季度更新时间表。所有四个周期都从2026年4月6日开始,分别在7月5日、10月5日、1月5日和4月5日结束,截止日期分别为8月7日、11月7日、2月7日和5月7日。

这种设计还有一个不错的副作用:如果用户在之前的某个季度发现了错误,那么在后续的更新中就可以直接进行修正。HMRC提供的端到端服务指南中也明确指出:每次更新都会使之前的数据失效,因为每个更新周期都是从4月6日开始的。

对于那些会计年度从4月1日开始的客户,他们也可以选择按日历周期来申报(例如4月1日至6月30日等),且截止日期同样不变。您无需将这两种方式硬编码在程序中,因为Obligations API会返回准确的日期信息。

英国税务海关总署将每一项需要更新的资料称为“义务事项”:对于每个从事自雇或房地产经营的业务来说,每个纳税年度会有四项这样的义务事项,再加上每年一次的纳税申报。

以下是您即将完成的整个处理流程:

应用程序与英国税务海关总署API之间进行季度数据更新的序列图。1:列出相关业务以获取businessId;2:获取未结清的义务事项信息,从而得到对应的期限和截止日期;3:提交累计摘要信息,系统会返回204作为响应;4:触发年度计算流程,系统会返回202以及一个计算ID;5:等待至少五秒后,再次检索计算结果,如果收到404错误代码,则重新尝试,直到得到200作为正常响应。

步骤1:如何获取业务ID

每个与自雇相关的接口请求都需要一个businessId,这是英国税务海关总署用于识别某项收入来源的标识符。如果一个人既从事自雇活动,又拥有房产进行出租,那么他就会有两个这样的ID。您可以通过Business Details API,使用客户的国民保险号码来获取这些ID:

// 使用Business Details API v2.0获取信息
const result = await request(
  'GET',
  '/individuals/business/details/' + nino + '/list',
  accessToken,
  req,
  null,
  '2.0',
);

const businesses = result.listOfBusinesses ?? [];
const soleTrade = businesses.find((b) => b.typeOfBusiness === 'self-employment');
const businessId = soleTrade?.businessId;

这些信息存储在listOfBusinesses数组中,每个条目都包含typeOfBusiness(如“self-employment”、“uk-property”等)、businessId,有时还会包含tradingName。在沙箱测试环境中,自雇业务的ID通常形如XBIS12345678901。

英国税务海关总署的建议是直接存储这些ID,而无需在每次调用接口之前都去查询它们。由于这个列表很少会发生变化,通常只有当客户新增或停止某项业务时才会更新,因此您可以在客户连接系统时或者他们主动询问时才去刷新这个列表。

步骤2:如何了解应缴纳的税款金额

接下来,您可以使用Obligations API来查询哪些期限内的义务事项尚未结清。这个接口目前处于3.0版本:

// 使用Obligations API v3.0获取信息
const raw = await request(
  'GET',
  '/obligations/details/' + nino + '/income-and-expenditure?status=open',
  accessToken,
  req,
  null,
  '3.0',
);

这些义务按照业务类型进行分类,相关日期信息则嵌套在下一层级中:

{
  "obligations": [
    {
      "typeOfBusiness": "self-employment",
      "businessId": "XBIS12345678901",
      "obligationDetails": [
        {
          "periodStartDate": "2026-04-06",
          "periodEndDate": "2026-10-05",
          "dueDate": "2026-11-07",
          "status": "open"
        }
      ]
    }
  ]
}

在用户界面中,这种嵌套结构会很快导致显示效果变得混乱,因此我将其优化为每项义务只显示在一行中,并将相关业务信息也放在同一行中;同时,我会筛选出那些状态为“open”且到期日期最早的义务:

function flattenObligations(raw, businessId) {
  return (raw.obligations ?? [])
    .filter((group) => group.businessId === businessId)
    .flatMap((group) =>
      (group(obligationDetails ?? []).map((d) => ({
        businessId: group.businessId,
        periodStartDate: d.periodStartDate,
        periodEndDate: d-periodEndDate,
        dueDate: d.dueDate,
        status: (d.status ?? '').toLowerCase() === 'fulfilled' ? 'fulfilled' : 'open',
      }),
    );
}

const next = flattenObligations(raw, businessId)
  .filter((o) => o.status === 'open')
  .sort((a, b) => a.dueDate.localeCompare(b.dueDate))[0];

在根据这些数据构建用户界面之前,有 three 点需要注意:

首先,这些义务并没有专门的“周期标识键”;它们的开始日期和结束日期实际上就代表了相应的统计周期,而你在第三步中需要返回的也正是这些信息。如果你需要一个固定的键值对,可以从这两个日期中推导出一个。

其次,应该直接从响应数据中获取 dueDate ,而不是自行计算它。因为这些日期是由 HMRC 设定的,而且他们之前也曾经更改过这些规定,所以应该以 API 提供的数据为准,而不要在你的代码中编写自定义的日期计算逻辑。

第三,过滤条件也有相应的规则:fromDate 和 toDate 必须一起发送,并且两者之间的时间间隔不得超过 366 天;此外,使用 businessId 进行过滤时,也需要提供 typeOfBusiness 参数。像上面那样,在你的代码中先获取所有状态为“open”的义务信息,然后再进行过滤处理,就可以避开这两个问题。

步骤 3:如何构建累计汇总报表

现在来看数据内容。自我雇佣业务 API 将这种报表称为“累计周期汇总报表”,它由四个部分组成:

  • periodDates:必填项。指的是你正在报告的统计周期的开始日期和结束日期,这些数据直接来源于各项义务信息。

  • periodIncome:包括 turnover(收入、费用和销售额)、other 类别的业务收入,以及 taxTakenOffTradingIncome。

  • periodExpenses:可以选择提供 consolidatedExpenses 的合计数值,也可以进行详细分类列出。

  • periodDisallowableExpenses:指各项明细费用中那些不能用于抵扣税款的部分。

以下是2026-27年度第二季度的完整、有效的申报表格,其中所有费用均以合并后的数值进行呈现:

{
  "periodDates": {
    "periodStartDate": "2026-04-06",
    "periodEndDate": "2026-10-05"
  },
  "periodIncome": {
    "turnover": 28450,
    "other": 0
  },
  "periodExpenses": {
    "consolidatedExpenses": 4310.45
  }
}

在详细列出的费用表格中,这些合并后的数值会被分解为与自我评估表格中的分类相对应的各项,例如costOfGoods、carVanTravelExpenses、adminCosts以及professionalFees。如果某项费用中包含个人开支,那么不允许计入申报的部分应被记录在相应的字段中:

"periodExpenses": {
  "costOfGoods": 2100,
  "carVanTravelExpenses": 1640.2,
  "adminCosts": 185.99
},
"periodDisallowableExpenses": {
  "carVanTravelExpensesDisallowable": 410.05
}

这两种申报方式不能同时使用。如果既提交了consolidatedExpenses,又提供了详细列出的费用信息,系统会返回错误提示RULE_both_EXPENSES_supPLIED。由于每项不允许计入申报的费用都对应着详细的分类项目,因此这些不允许扣除的费用应通过详细列出的表格进行申报。

那么,在什么情况下可以使用合并后的申报方式呢?根据英国税务海关总署的服务指南,年营业额低于90,000英镑的纳税人可以只提交一项合计费用金额。由于每次更新都会累加到之前的数据中,因此在实际操作时,需要在每次提交申报资料时,将当年度至今的营业额与这个门槛进行比较。一旦营业额达到90,000英镑,就必须开始详细列明各项费用。我在数据发送给税务部门之前,会在服务器上执行这一检查:

const CONSOLIDATED_EXPENSES_TURNOVER_LIMIT = 90_000;

function checkConsolidatedExpensesLimit({ turnover, usesConsolidatedExpenses }) {
  if (!usesConsolidatedExpenses) return null;
  if ((turnover ?? 0) >= CONSOLIDATED_EXPENSES_TURNOVER_LIMIT) {
    return (
      '当营业额达到90,000英镑时,不得再使用合并后的费用申报方式。'
      '请详细列明各项费用。'
    );
  }
  return null;
}

最后,请注意,所有金额最多只能保留两位小数,因此在将数据发送之前,请先对各项数值进行四舍五入处理(我使用的是Number(total.toFixed(2))这个方法)。

步骤4:如何提交更新信息

一旦完成了表格的填写,提交申报数据只需要执行一个PUT请求即可。税年度信息会以YYYY-YY的形式包含在路径中,而具体的期间日期则应放在申报数据的主体部分,而不是URL中:

// PUT /individuals/business/self-employment/{nino}/{businessId}/cumulative/{taxYear}
// 自雇业务API v5.0
await request(
  'PUT',
  '/individuals/business/self-employment/' + nino + '/' + businessId + '/cumulative/' + taxYear,
  accessToken,
  req,
  summary,
  '5.0',
);
成功的结果会是“204 No Content”,且不会返回任何收据编号。因此,您需要自行记录自己发送了哪些信息、发送时间以及英国税务海关总署给出的回复状态。日后您可以通过在同一路径上使用“GET”请求来查看英国税务海关总署保存的副本。 由于这是一种“PUT”请求,因此同一次调用既可以创建数据,也可以修改数据。如果针对同一个纳税年度重新提交请求,就会替换英国税务海关总署之前保存的信息。这种累积式的处理方式正是设计初衷所期望的,但也正是导致这个流程中出现最严重问题的原因所在(请参阅相关注意事项)。 这个接口仅接受2025-26纳税年度及之后的数据。那些使用“POST”请求访问“/period”接口的旧教程,描述的是早期的按周期处理的数据模型。

步骤5:如何触发并获取税收计算结果

在数据更新后,客户们想知道自己大概需要缴纳多少税款。个人税收计算API可以异步地满足这一需求:您先触发计算流程,获取一个计算ID,稍后再去获取最终结果。在路径中,计算类型会作为参数出现在末尾;在季度数据更新后,应该使用“in-year”这个参数:
// 使用“POST”请求
const { calculationId } = await request(
  'POST',
  '/individuals/calculations/' + nino + '/self-assessment/' + taxYear + '/trigger/in-year',
  accessToken,
  req,
  {}, // 必须是一个空对象,不能为null(请参阅相关注意事项)
  '8.0',
);
英国税务海关总署的接口文档建议,在尝试获取结果之前至少等待五秒钟。在计算结果准备好之前,该接口会返回“404 Not Found”错误代码,因此可以通过一个简单的重试机制来处理这种情况:
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function waitForCalculation(nino, taxYear, calculationId, accessToken, req) {
  const path =
    '/individuals/calculations/' + nino + '/self-assessment/' + taxYear + '/' + calculationId;
  await sleep(5000); // 英国税务海关总署建议在触发计算后至少等待五秒钟

  for (let attempt = 0; attempt < 5; attempt += 1) {
    try {
      return await request('GET', path, accessToken, req, null, '8.0');
    } catch (err) {
      // 只有当返回码为404时,才表示计算结果尚未准备好;其他错误都需要被重新抛出。
      if (err.response?.status !== 404 || attempt === 4) throw err;
    }
    await sleep(1000 * (attempt + 1));
  }
}
最终返回的结果会包含英国税务海关总署所掌握的各类收入信息,但在进行季度数据更新时,实际上只需要填写少数几个字段即可。以下是一个简化的响应示例,并附有说明性数据:
{
  "metadata": {
    "calculationId": "f2fb30e5-4ab6-4a29-b3c1-c7264259ff1c",
    "taxYear": "2026-27",
    "calculationType": "in-year",
    "periodFrom": "2026-04-06",
    "periodTo": "2026-10-05"
  },
  "calculation": {
    "taxCalculation": {
      "totalIncomeTaxAndNicsDue": 3902.6
    }
  }
}

calculation.taxCalculation.totalIncomeTaxAndNicsDue 就是最终得出的数值。metadata.periodTo 表明了这些数据的统计期限:它是截至最近一次数据提交时的时间点,而不是您所询问的具体日期。

如果提交的数据未能通过英国税务海关总署的审核,那么根本就不会进行任何计算。此时,messages.errors 中会包含一系列 { id, text } 形式的错误信息,您应该将这些信息展示给客户,以便他们能够及时更正自己的记录。

在向客户展示任何年度内的统计数据之前,根据英国税务海关总署的规定,必须添加免责声明。英国税务海关总署的服务指南中提供了相应的格式文本,或者您也可以自行编写:

“此计算结果仅基于英国税务海关总署所掌握的、截至20XX-XX-XX日期您的收入和支出信息。在纳税年度内,随着我们收到更多关于您的资料,这些数据可能会发生变化。”

请使用 periodTo 中指定的日期进行计算。同时,也应该明确说明这是一个估算值——英国税务海关总署的指南中明确指出,在当前阶段客户无需支付任何费用。

常见问题

1. 默认的沙箱数据已经过时了

如果在沙箱环境中使用 Obligations API 而不指定测试场景,那么系统会返回上一个纳税年度的静态数据,而累积数据端点会以 RULE_TAX_YEAR_NOT_SUPPORTED 作为响应来拒绝此类请求。因此,请务必发送 Gov-Test-Scenario: DYNAMIC 以便获取当前年度的实时数据。

默认情况下,累积数据端点也不具备状态保持功能:在您发送 PUT 请求之后,再次发送 GET 请求仍然会得到预先设定好的数据。如果要实现真正意义上的双向交互,请使用通过 Self Assessment Test Support API 创建的测试账户,并选择 STATEFUL 测试场景。

2. 新的更新会替换旧的数据

由于每次发送 PUT 请求都会覆盖之前的数据,因此如果表格初始为空且用户只填写了部分内容,那么之前季度的数据就会被覆盖。为了确保每次提交的数据都是最新的,请先通过检索端点或您自己的记录来预填充表格内容,这样每次重新提交时都会从完整的年度累计数值开始计算。

3>请填写零值,不要遗漏任何字段

英国税务海关总署的端点文档中明确指出,即使某些数值为零,也必须在提交数据时填写相应的收入和支出值。因此,对于没有收入的季度,仍然需要将 turnover 和 other 的数值设置为 0。此外,GOV.UK 也要求,即使某个时间段内没有发生任何交易,客户也必须提交更新数据,因此不要因为所有数值都是零就拒绝提交请求。

4. 不能过早提交申请

累计数据端点会拒绝在期限结束前10天以上提交的申请(RULE_EARLY_DATA_SUBMISSION_NOT_ACCEPTED),也会拒绝提交日期早于已提交申请的日期的请求(RULE_SUBMISSION_END_DATE_CANNOT_MOVE_BACKWARD)。因此,请明确显示期限结束日期,并且只有在该日期到来时才启用提交按钮。

5. “已完成”状态可能需要一小时才能更新

在成功收到204响应后,HMRC可能需要长达一小时的时间来将义务状态标记为“已完成”,因此即使立即重新查询,系统仍会显示该义务处于未完成状态。请相信自己记录的提交信息,并告知客户状态很快就会更新。

6. 使用空对象触发计算

在我的测试中,使用null作为请求体发送触发请求时,HMRC的计算后端会返回500错误代码;而使用空的JSON对象({})进行请求时则能够成功提交。另外,请注意:个人计算服务的9.0版本目前还在测试环境中,但在本文撰写时,8.0版本已经可以在生产环境中使用了。

下一步该怎么做

现在您已经掌握了完整的季度操作流程:首先找到相关企业信息,查看其未完成的义务事项,然后生成符合规定的累计汇总报告并提交申请,之后再触发计算过程,等待结果出来,并仔细阅读其中附带的注意事项。

在进行第四次更新后,所有操作仍然可以通过Individual Calculations API来完成:您需要触发intent-to-finalise计算流程而非in-year计算流程,将计算结果展示给客户,然后让他们根据该计算结果ID提交最终申报文件。

这个流程本身就值得专门编写一篇教程,这也是我接下来打算做的事情。

在正式开始实施之前,请注意一个实际问题:HMRC的API页面目前明确表示,不再接受针对2026-27年度季度更新功能的生产环境认证申请。虽然测试环境仍然开放,因此您今天还可以继续进行上述所有的测试工作,但在确定正式上线日期之前,请务必阅读自我雇佣企业API页面上的相关通知。

我目前负责开发TapTax这款专为英国个体经营者设计的税务数字化应用,本文中提到的代码也正是来自这个项目。

关于作者:所罗门·阿莫斯是TapTax的创始人,他负责推动了该公司与英国税务海关总署之间的“数字化纳税”系统集成项目。在过去的三年里,他一直担任英国税务海关总署数字化现代化项目的首席技术架构师,同时拥有工程学博士学位,其研究方向为机器学习。您可以通过以下链接找到他的信息: LinkedIn。

相关文章

技术实践

如何使用 Next.js 和 Jev 构建一个能够自动将错误信息发送到 GitHub 的人工智能支持系统

每个网站都会收到用户的反馈,而其中大部分反馈最终都会被搁置在某个不太合适的地方。有的访客会发现某个按钮无法正常使用,然后会通过电子邮件联系你;还有人会在社交媒体上留言,反映他们的手机无法打开某个页面;再有人则会填写你的联系表格,提出一些功能改进的建议,而这些建议就会和新闻通讯、收据之类的信息一起堆在你的收件箱里。 当你终于坐下来准备处理这些反馈时,你会发现那些错误报告分散在三个不同的地方。其中有一半的报告缺少必要的详细信息,而那些被上传到GitHub上的报告,也是有人手动复制过去的,有时甚至还会把访客的电子邮件地址也粘贴在里面。 因为我想为自己的项目提供更好的支持系统,所以我决定自己动手开发它

阅读全文
技术实践

如何利用Claude API构建一个可靠的人工智能助手

大型语言模型能够回答问题、总结文档、编写代码,还能与外部系统进行交互。但构建一个可靠的人工智能应用,仅仅发送指令并显示响应是远远不够的。 一个可用于实际生产的应用程序必须能够管理对话历史记录、提供相关的上下文信息、安全地使用各种工具、处理不同类型的响应,并判断生成的内容是否有用。 在本教程中,我们将构建 ShopHelper ——这个为虚构在线商店设计的客户支持助手。完成制作后,ShopHelper将能够做到以下几件事: 以统一的语气回答常见问题 记住顾客之前说过的话 通过调用代码中的函数来查询订单状态 安全地处理Claude给出的多段式响应 利用工作流程来处理客户支持请求 评估修改提示语后效

阅读全文
技术实践

如何使用Next.js、Supabase以及TypeSafe Jev来构建一个用于筛选人工智能相关简历的工具

每当我们发布一份工程招聘启事时,一周内就会收到300到400份简历。仔细阅读每一份简历大约需要两分钟的时间。因此,仅仅为了处理一个职位空缺,我们在开始任何面试之前就已经要花费11个小时来处理这些简历了。 但实际上,并没有人会真的去阅读每一份简历。相反,人力资源部门会进行快速的筛选工作。他们会快速浏览应聘者的职位名称、工作经验年限以及所掌握的技术框架,然后在大约15秒的时间内,将简历分为“需要进一步查看”和“可能不符合要求”两类。当他们看到第40份简历时,他们对这份简历的关注程度就已经远远低于对第4份简历时的关注了。 我们的目标就是取代这种快速筛选的过程,而不是放弃阅读简历这一环节。当阅读简历确

阅读全文
技术实践

对象关系映射框架为何仍会允许SQL注入攻击的发生(以及如何弥补这些安全漏洞)

许多开发人员认为,一旦使用了对象关系映射工具,SQL注入问题就不再存在了。但实际上,这个问题并没有消失,只是转移到了其他地方。 像Sequelize、Prisma、TypeORM和Knex这样的对象关系映射工具会自动为你生成参数化的查询语句,这一功能确实非常实用。但问题往往出现在那些超出了这些工具安全处理范围的代码部分——比如那些无法被对象关系映射工具的查询构建器清晰表达的原始SQL语句、动态排序字段,或者那些被不加思考就直接使用的辅助函数。 恰恰就是这些地方,会成为攻击者首先关注的目标。而且由于应用程序的其他部分看起来都受到了保护,开发者很容易忽视这些问题。 本文将重点讨论四种这类常见的安全

阅读全文