{"id":629,"date":"2025-04-05T11:43:17","date_gmt":"2025-04-05T03:43:17","guid":{"rendered":"https:\/\/www.hyy.net\/?p=629"},"modified":"2025-04-05T11:43:17","modified_gmt":"2025-04-05T03:43:17","slug":"asp-net-core-in-action-29-improving-your-applications-security","status":"publish","type":"post","link":"https:\/\/diji.net\/?p=629","title":{"rendered":"ASP.NET Core in Action 29 Improving your application\u2019s security"},"content":{"rendered":"<p>29 Improving your application\u2019s security<br \/>\n29 \u63d0\u9ad8\u5e94\u7528\u7a0b\u5e8f\u7684\u5b89\u5168\u6027<\/p>\n<p>This chapter covers<br \/>\n\u672c\u7ae0\u6db5\u76d6<\/p>\n<p>\u2022  Defending against cross-site scripting attacks<br \/>\n\u9632\u5fa1\u8de8\u7ad9\u70b9\u811a\u672c\u653b\u51fb<\/p>\n<p>\u2022  Protecting from cross-site request forgery attacks<br \/>\n\u9632\u6b62\u8de8\u7ad9\u70b9\u8bf7\u6c42\u4f2a\u9020\u653b\u51fb<\/p>\n<p>\u2022  Allowing calls to your API from other apps using CORS<br \/>\n\u5141\u8bb8\u4f7f\u7528 CORS\u4ece\u5176\u4ed6\u5e94\u7528\u7a0b\u5e8f\u8c03\u7528\u60a8\u7684 API <\/p>\n<p>\u2022  Avoiding attach vectors such as SQL injection attacks<br \/>\n\u907f\u514d SQL \u6ce8\u5165\u653b\u51fb\u7b49\u9644\u52a0\u5411\u91cf<\/p>\n<p>In chapter 28 you learned how and why you should use HTTPS in your application: to protect your HTTP requests from attackers. In this chapter we look at more ways to protect your application and your application\u2019s users from attackers. Because security is an extremely broad topic that covers lots of avenues, this chapter is by no means an exhaustive guide. It\u2019s intended to make you aware of some of the most common threats to your app and how to counteract them, and also to highlight areas where you can inadvertently introduce vulnerabilities if you\u2019re not careful.<br \/>\n\u5728\u7b2c 28 \u7ae0\u4e2d\uff0c\u60a8\u4e86\u89e3\u4e86\u5982\u4f55\u4ee5\u53ca\u4e3a\u4ec0\u4e48\u5e94\u8be5\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4f7f\u7528 HTTPS\uff1a\u4fdd\u62a4\u60a8\u7684 HTTP \u8bf7\u6c42\u514d\u53d7\u653b\u51fb\u8005\u7684\u653b\u51fb\u3002\u5728\u672c\u7ae0\u4e2d\uff0c\u6211\u4eec\u5c06\u4ecb\u7ecd\u66f4\u591a\u65b9\u6cd5\u6765\u4fdd\u62a4\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u548c\u5e94\u7528\u7a0b\u5e8f\u7528\u6237\u514d\u53d7\u653b\u51fb\u8005\u7684\u653b\u51fb\u3002\u7531\u4e8e\u5b89\u5168\u6027\u662f\u4e00\u4e2a\u975e\u5e38\u5e7f\u6cdb\u7684\u4e3b\u9898\uff0c\u6db5\u76d6\u4e86\u8bb8\u591a\u9014\u5f84\uff0c\u56e0\u6b64\u672c\u7ae0\u7edd\u4e0d\u662f\u8be6\u5c3d\u7684\u6307\u5357\u3002\u5b83\u65e8\u5728\u8ba9\u60a8\u4e86\u89e3\u5e94\u7528\u7a0b\u5e8f\u9762\u4e34\u7684\u4e00\u4e9b\u6700\u5e38\u89c1\u5a01\u80c1\u4ee5\u53ca\u5982\u4f55\u5e94\u5bf9\u8fd9\u4e9b\u5a01\u80c1\uff0c\u5e76\u7a81\u51fa\u663e\u793a\u5982\u679c\u60a8\u4e0d\u5c0f\u5fc3\u53ef\u80fd\u4f1a\u65e0\u610f\u4e2d\u5f15\u5165\u6f0f\u6d1e\u7684\u533a\u57df\u3002<\/p>\n<p><b>TIP<\/b>  I strongly advise exploring additional resources around security after you\u2019ve read this chapter. The Open Web Application Security Project (OWASP) (www.owasp.org) is an excellent resource. Alternatively, Troy Hunt has some excellent courses and workshops on security, geared toward .NET developers (<a href=\"https:\/\/www.troyhunt.com\">https:\/\/www.troyhunt.com<\/a>).<br \/>\n\u63d0\u793a:\u6211\u5f3a\u70c8\u5efa\u8bae\u60a8\u5728\u9605\u8bfb\u672c\u7ae0\u540e\u63a2\u7d22\u6709\u5173\u5b89\u5168\u6027\u7684\u5176\u4ed6\u8d44\u6e90\u3002\u5f00\u653e Web \u5e94\u7528\u7a0b\u5e8f\u5b89\u5168\u9879\u76ee \uff08OWASP\uff09 \uff08www.owasp.org\uff09 \u662f\u4e00\u4e2a\u5f88\u597d\u7684\u8d44\u6e90\u3002\u6216\u8005\uff0cTroy Hunt \u6709\u4e00\u4e9b\u9762\u5411 .NET \u5f00\u53d1\u4eba\u5458 \uff08<a href=\"https:\/\/www.troyhunt.com\">https:\/\/www.troyhunt.com<\/a>\uff09 \u7684\u4f18\u79c0\u5b89\u5168\u8bfe\u7a0b\u548c\u7814\u8ba8\u4f1a\u3002<\/p>\n<p>In sections 29.1 and 29.2 you\u2019ll start by learning about two potential attacks that should be on your radar: cross-site scripting (XSS) and cross-site request forgery (CSRF). We\u2019ll explore how the attacks work and how you can prevent them in your apps. ASP.NET Core has built-in protection against both types of attacks, but you have to remember to use the protection correctly and resist the temptation to circumvent it unless you\u2019re certain it\u2019s safe to do so.<br \/>\n\u5728\u7b2c 29.1 \u8282\u548c\u7b2c 29.2 \u8282\u4e2d\uff0c\u60a8\u5c06\u9996\u5148\u4e86\u89e3\u5e94\u8be5\u5f15\u8d77\u6ce8\u610f\u7684\u4e24\u79cd\u6f5c\u5728\u653b\u51fb\uff1a\u8de8\u7ad9\u70b9\u811a\u672c \uff08XSS\uff09 \u548c\u8de8\u7ad9\u70b9\u8bf7\u6c42\u4f2a\u9020 \uff08CSRF\uff09\u3002\u6211\u4eec\u5c06\u63a2\u8ba8\u8fd9\u4e9b\u653b\u51fb\u7684\u5de5\u4f5c\u539f\u7406\uff0c\u4ee5\u53ca\u5982\u4f55\u5728\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u9632\u6b62\u5b83\u4eec\u3002ASP.NET Core \u5177\u6709\u9488\u5bf9\u8fd9\u4e24\u79cd\u7c7b\u578b\u7684\u653b\u51fb\u7684\u5185\u7f6e\u4fdd\u62a4\uff0c\u4f46\u60a8\u5fc5\u987b\u8bb0\u4f4f\u6b63\u786e\u4f7f\u7528\u4fdd\u62a4\u5e76\u62b5\u5236\u89c4\u907f\u5b83\u7684\u8bf1\u60d1\uff0c\u9664\u975e\u60a8\u786e\u5b9a\u8fd9\u6837\u505a\u662f\u5b89\u5168\u7684\u3002<\/p>\n<p>Section 29.3 deals with a common scenario: you have an application that wants to use JavaScript requests to retrieve data from a second app. By default, web browsers block requests to other apps, so you need to enable cross-origin resource sharing (CORS) in your API to achieve this. We\u2019ll look at how CORS works, how to create a CORS policy for your app, and how to apply it to speci\ufb01c endpoints.<br \/>\nSection 29.3 \u5904\u7406\u4e00\u4e2a\u5e38\u89c1\u60c5\u51b5\uff1a\u60a8\u6709\u4e00\u4e2a\u5e94\u7528\u7a0b\u5e8f\uff0c\u5b83\u60f3\u8981\u4f7f\u7528 JavaScript \u8bf7\u6c42\u4ece\u7b2c\u4e8c\u4e2a\u5e94\u7528\u7a0b\u5e8f\u68c0\u7d22\u6570\u636e\u3002\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0cWeb \u6d4f\u89c8\u5668\u4f1a\u963b\u6b62\u5bf9\u5176\u4ed6\u5e94\u7528\u7a0b\u5e8f\u7684\u8bf7\u6c42\uff0c\u56e0\u6b64\u60a8\u9700\u8981\u5728 API \u4e2d\u542f\u7528\u8de8\u57df\u8d44\u6e90\u5171\u4eab \uff08CORS\uff09 \u624d\u80fd\u5b9e\u73b0\u6b64\u76ee\u7684\u3002\u6211\u4eec\u5c06\u4e86\u89e3 CORS \u7684\u5de5\u4f5c\u539f\u7406\u3001\u5982\u4f55\u4e3a\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u521b\u5efa CORS \u7b56\u7565\u4ee5\u53ca\u5982\u4f55\u5c06\u5176\u5e94\u7528\u4e8e\u7279\u5b9a\u7ec8\u7aef\u8282\u70b9\u3002<\/p>\n<p>The \ufb01nal section of this chapter, section 29.4, covers a collection of common threats to your application. Each one represents a potentially critical \ufb02aw that an attacker could use to compromise your application. The solutions to each threat are generally relatively simple; the important thing is to recognize where the \ufb02aws could exist in your own apps so you can ensure that you don\u2019t leave yourself vulnerable.<br \/>\n\u672c\u7ae0\u7684\u6700\u540e\u4e00\u90e8\u5206\uff0c\u5373 29.4 \u8282\uff0c\u6db5\u76d6\u4e86\u5e94\u7528\u7a0b\u5e8f\u7684\u4e00\u7cfb\u5217\u5e38\u89c1\u5a01\u80c1\u3002\u6bcf\u4e2a\u6f0f\u6d1e\u90fd\u4ee3\u8868\u4e00\u4e2a\u6f5c\u5728\u7684\u4e25\u91cd\u7f3a\u9677\uff0c\u653b\u51fb\u8005\u53ef\u4ee5\u5229\u7528\u8be5\u6f0f\u6d1e\u6765\u7834\u574f\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u3002\u6bcf\u79cd\u5a01\u80c1\u7684\u89e3\u51b3\u65b9\u6848\u901a\u5e38\u76f8\u5bf9\u7b80\u5355;\u91cd\u8981\u7684\u662f\u8bc6\u522b\u60a8\u81ea\u5df1\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u53ef\u80fd\u5b58\u5728\u7684\u7f3a\u9677\uff0c\u8fd9\u6837\u60a8\u5c31\u53ef\u4ee5\u786e\u4fdd\u4e0d\u4f1a\u8ba9\u81ea\u5df1\u5bb9\u6613\u53d7\u5230\u653b\u51fb\u3002<\/p>\n<p>As I mentioned in chapter 28, you should always start by adding HTTPS to your app to encrypt the tra\ufb03c between your users\u2019 browsers and your app. Without HTTPS, attackers could subvert many of the safeguards you add to your app, so it\u2019s an important \ufb01rst step to take.<br \/>\n\u6b63\u5982\u6211\u5728\u7b2c 28 \u7ae0\u4e2d\u63d0\u5230\u7684\uff0c\u60a8\u5e94\u8be5\u59cb\u7ec8\u4ece\u5c06 HTTPS \u6dfb\u52a0\u5230\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u5f00\u59cb\uff0c\u4ee5\u52a0\u5bc6\u7528\u6237\u6d4f\u89c8\u5668\u548c\u5e94\u7528\u7a0b\u5e8f\u4e4b\u95f4\u7684\u6d41\u91cf\u3002\u5982\u679c\u6ca1\u6709 HTTPS\uff0c\u653b\u51fb\u8005\u53ef\u80fd\u4f1a\u7834\u574f\u60a8\u6dfb\u52a0\u5230\u5e94\u7528\u7a0b\u5e8f\u7684\u8bb8\u591a\u4fdd\u62a4\u63aa\u65bd\uff0c\u56e0\u6b64\u8fd9\u662f\u91cd\u8981\u7684\u7b2c\u4e00\u6b65\u3002<\/p>\n<p>Unfortunately, most other security practices require rather more vigilance to ensure that you don\u2019t accidentally introduce vulnerabilities into your app as it grows and develops. Many attacks are conceptually simple and have been known about for years, yet they\u2019re still commonly found in new applications. In the next section we\u2019ll look at one such attack and see how to defend against it when building apps using Razor Pages.<br \/>\n\u4e0d\u5e78\u7684\u662f\uff0c\u5927\u591a\u6570\u5176\u4ed6\u5b89\u5168\u5b9e\u8df5\u90fd\u9700\u8981\u66f4\u52a0\u8b66\u60d5\uff0c\u4ee5\u786e\u4fdd\u60a8\u4e0d\u4f1a\u5728\u5e94\u7528\u7a0b\u5e8f\u7684\u6210\u957f\u548c\u53d1\u5c55\u8fc7\u7a0b\u4e2d\u610f\u5916\u5730\u5c06\u6f0f\u6d1e\u5f15\u5165\u5e94\u7528\u7a0b\u5e8f\u3002\u8bb8\u591a\u653b\u51fb\u5728\u6982\u5ff5\u4e0a\u5f88\u7b80\u5355\uff0c\u5e76\u4e14\u5df2\u7ecf\u4e3a\u4eba\u6240\u77e5\u591a\u5e74\uff0c\u4f46\u5b83\u4eec\u4ecd\u7136\u5e38\u89c1\u4e8e\u65b0\u5e94\u7528\u7a0b\u5e8f\u3002\u5728\u4e0b\u4e00\u8282\u4e2d\uff0c\u6211\u4eec\u5c06\u4ecb\u7ecd\u4e00\u79cd\u6b64\u7c7b\u653b\u51fb\uff0c\u5e76\u4e86\u89e3\u5982\u4f55\u5728\u4f7f\u7528 Razor Pages \u6784\u5efa\u5e94\u7528\u7a0b\u5e8f\u65f6\u9632\u5fa1\u5b83\u3002<\/p>\n<h2>29.1 Defending against cross- site scripting (XSS) attacks\u200c<\/h2>\n<p>29.1 \u9632\u5fa1\u8de8\u7ad9\u70b9\u811a\u672c \uff08XSS\uff09 \u653b\u51fb<\/p>\n<p>In this section I describe XSS attacks and how attackers can use them to compromise your users. I show how the Razor Pages framework protects you from these attacks, how to disable the protections when you need to, and what to look out for. I also discuss the di\ufb00erence between HTML encoding and JavaScript encoding, and the e\ufb00ect of using the wrong encoder.\u200c<br \/>\n\u5728\u672c\u8282\u4e2d\uff0c\u6211\u5c06\u4ecb\u7ecd XSS \u653b\u51fb\u4ee5\u53ca\u653b\u51fb\u8005\u5982\u4f55\u5229\u7528\u5b83\u4eec\u6765\u5371\u5bb3\u60a8\u7684\u7528\u6237\u3002\u6211\u5c06\u5c55\u793a Razor Pages \u6846\u67b6\u5982\u4f55\u4fdd\u62a4\u60a8\u514d\u53d7\u8fd9\u4e9b\u653b\u51fb\uff0c\u5982\u4f55\u5728\u9700\u8981\u65f6\u7981\u7528\u4fdd\u62a4\uff0c\u4ee5\u53ca\u9700\u8981\u6ce8\u610f\u7684\u4e8b\u9879\u3002\u6211\u8fd8\u8ba8\u8bba\u4e86 HTML \u7f16\u7801\u548c JavaScript \u7f16\u7801\u4e4b\u95f4\u7684\u533a\u522b\uff0c\u4ee5\u53ca\u4f7f\u7528\u9519\u8bef\u7f16\u7801\u5668\u7684\u5f71\u54cd\u3002<\/p>\n<p>Attackers can exploit a vulnerability in your app to create XSS attacks that execute code in another user\u2019s browser. Commonly, attackers submit content using a legitimate approach, such as an input form, that is later rendered somewhere to the page. By carefully crafting malicious input, the attacker can execute arbitrary JavaScript on a user\u2019s browser and so can steal cookies, impersonate the user, and generally do bad things.<br \/>\n\u653b\u51fb\u8005\u53ef\u4ee5\u5229\u7528\u60a8\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u6f0f\u6d1e\u6765\u521b\u5efa XSS \u653b\u51fb\uff0c\u4ece\u800c\u5728\u5176\u4ed6\u7528\u6237\u7684\u6d4f\u89c8\u5668\u4e2d\u6267\u884c\u4ee3\u7801\u3002\u901a\u5e38\uff0c\u653b\u51fb\u8005\u4f7f\u7528\u5408\u6cd5\u65b9\u6cd5\uff08\u5982\u8f93\u5165\u8868\u5355\uff09\u63d0\u4ea4\u5185\u5bb9\uff0c\u8fd9\u4e9b\u65b9\u6cd5\u7a0d\u540e\u4f1a\u5448\u73b0\u5728\u9875\u9762\u7684\u67d0\u4e2a\u4f4d\u7f6e\u3002\u901a\u8fc7\u7cbe\u5fc3\u8bbe\u8ba1\u6076\u610f\u8f93\u5165\uff0c\u653b\u51fb\u8005\u53ef\u4ee5\u5728\u7528\u6237\u7684\u6d4f\u89c8\u5668\u4e0a\u6267\u884c\u4efb\u610f JavaScript\uff0c\u4ece\u800c\u7a83\u53d6 Cookie\u3001\u5192\u5145\u7528\u6237\uff0c\u5e76\u901a\u5e38\u4f1a\u505a\u574f\u4e8b\u3002<\/p>\n<p><b>TIP<\/b> For a detailed discussion of XSS attacks, see the \u201cCross Site Scripting (XSS)\u201d article on the OWASP site: <a href=\"https:\/\/owasp.org\/www-community\/attacks\/xss\">https:\/\/owasp.org\/www-community\/attacks\/xss<\/a>.<br \/>\n\u63d0\u793a:\u6709\u5173 XSS \u653b\u51fb\u7684\u8be6\u7ec6\u8ba8\u8bba\uff0c\u8bf7\u53c2\u9605 OWASP \u7ad9\u70b9\u4e0a\u7684\u201c\u8de8\u7ad9\u70b9\u811a\u672c \uff08XSS\uff09\u201d\u6587\u7ae0\uff1a<a href=\"https:\/\/owasp.org\/www-community\/attacks\/xss\">https:\/\/owasp.org\/www-community\/attacks\/xss<\/a>\u3002<\/p>\n<p>Figure 29.1 shows a basic example of an XSS attack. Legitimate users of your app can send their name to your app by submitting a form. The app then adds the name to an internal list and renders the whole list to the page. If the names are not rendered safely, a malicious user can execute JavaScript in the browser of every other user who views the list.<br \/>\n\u56fe 29.1 \u663e\u793a\u4e86 XSS \u653b\u51fb\u7684\u4e00\u4e2a\u57fa\u672c\u793a\u4f8b\u3002\u60a8\u5e94\u7528\u7684\u5408\u6cd5\u7528\u6237\u53ef\u4ee5\u901a\u8fc7\u63d0\u4ea4\u8868\u5355\u5c06\u5176\u540d\u79f0\u53d1\u9001\u5230\u60a8\u7684\u5e94\u7528\u3002\u7136\u540e\uff0c\u5e94\u7528\u7a0b\u5e8f\u5c06\u540d\u79f0\u6dfb\u52a0\u5230\u5185\u90e8\u5217\u8868\uff0c\u5e76\u5c06\u6574\u4e2a\u5217\u8868\u5448\u73b0\u5230\u9875\u9762\u3002\u5982\u679c\u540d\u79f0\u672a\u5b89\u5168\u5448\u73b0\uff0c\u6076\u610f\u7528\u6237\u53ef\u4ee5\u5728\u67e5\u770b\u5217\u8868\u7684\u6240\u6709\u5176\u4ed6\u7528\u6237\u7684\u6d4f\u89c8\u5668\u4e2d\u6267\u884c JavaScript\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/2901.jpg\" alt=\"alt text\" \/><\/p>\n<p>Figure 29.1 How an XSS vulnerability is exploited. An attacker submits malicious content to your app, which is displayed in the browsers of other users. If the app doesn\u2019t encode the content when writing to the page, the input becomes part of the HTML of the page and can run arbitrary JavaScript.<br \/>\n\u56fe 29.1 XSS \u6f0f\u6d1e\u662f\u5982\u4f55\u88ab\u5229\u7528\u7684\u3002\u653b\u51fb\u8005\u5411\u60a8\u7684\u5e94\u7528\u63d0\u4ea4\u6076\u610f\u5185\u5bb9\uff0c\u8fd9\u4e9b\u5185\u5bb9\u4f1a\u663e\u793a\u5728\u5176\u4ed6\u7528\u6237\u7684\u6d4f\u89c8\u5668\u4e2d\u3002\u5982\u679c\u5e94\u7528\u7a0b\u5e8f\u5728\u5199\u5165\u9875\u9762\u65f6\u672a\u5bf9\u5185\u5bb9\u8fdb\u884c\u7f16\u7801\uff0c\u5219\u8f93\u5165\u5c06\u6210\u4e3a\u9875\u9762 HTML \u7684\u4e00\u90e8\u5206\uff0c\u5e76\u4e14\u53ef\u4ee5\u8fd0\u884c\u4efb\u610f JavaScript\u3002<\/p>\n<p>In \ufb01gure 29.1 the user entered a snippet of HTML, such as their name. When users view the list of names, the Razor template renders the names using @Html.Raw(), which writes the <code>&lt;script&gt;<\/code> tag directly to the document. The user\u2019s input has become part of the page\u2019s HTML structure. As soon as the page is loaded in a user\u2019s browser, the<code>&lt;script&gt;<\/code> tag executes, and the user is compromised. Once an attacker can execute arbitrary JavaScript on a user\u2019s browser, they can do pretty much anything.<br \/>\n\u5728\u56fe 29.1 \u4e2d\uff0c\u7528\u6237\u8f93\u5165\u4e86\u4e00\u4e2a HTML \u7247\u6bb5\uff0c\u4f8b\u5982\u4ed6\u4eec\u7684\u540d\u79f0\u3002\u5f53\u7528\u6237\u67e5\u770b\u540d\u79f0\u5217\u8868\u65f6\uff0cRazor \u6a21\u677f\u4f7f\u7528 @Html.Raw\uff08\uff09 \u5448\u73b0\u540d\u79f0\uff0c\u540e\u8005\u5c06<code>&lt;script&gt;<\/code>\u6807\u8bb0\u76f4\u63a5\u5199\u5165\u6587\u6863\u3002\u7528\u6237\u7684\u8f93\u5165\u5df2\u6210\u4e3a\u9875\u9762 HTML \u7ed3\u6784\u7684\u4e00\u90e8\u5206\u3002\u4e00\u65e6\u9875\u9762\u52a0\u8f7d\u5230\u7528\u6237\u7684\u6d4f\u89c8\u5668\u4e2d\uff0c<code>&lt;script&gt;<\/code>\u6807\u8bb0\u5c31\u4f1a\u6267\u884c\uff0c\u5e76\u4e14\u7528\u6237\u4f1a\u53d7\u5230\u5a01\u80c1\u3002\u4e00\u65e6\u653b\u51fb\u8005\u53ef\u4ee5\u5728\u7528\u6237\u7684\u6d4f\u89c8\u5668\u4e0a\u6267\u884c\u4efb\u610f JavaScript\uff0c\u4ed6\u4eec\u51e0\u4e4e\u53ef\u4ee5\u505a\u4efb\u4f55\u4e8b\u60c5\u3002<\/p>\n<p><b>TIP<\/b>  You can dramatically limit the control an attacker has even if they exploit an XSS vulnerability using a Content- Security-Policy (CSP). You can read about CSP at <a href=\"http:\/\/mng.bz\/nWW2\">http:\/\/mng.bz\/nWW2<\/a>. I have an open-source library you can use to integrate a CSP into your app available on NuGet at <a href=\"http:\/\/mng.bz\/vnn4\">http:\/\/mng.bz\/vnn4<\/a>.<br \/>\n\u63d0\u793a:\u60a8\u53ef\u4ee5\u6781\u5927\u5730\u9650\u5236\u653b\u51fb\u8005\u7684\u63a7\u5236\u6743\uff0c\u5373\u4f7f\u4ed6\u4eec\u4f7f\u7528\u5185\u5bb9\u5b89\u5168\u7b56\u7565 \uff08CSP\uff09 \u5229\u7528 XSS \u6f0f\u6d1e\u3002\u60a8\u53ef\u4ee5\u5728 <a href=\"http:\/\/mng.bz\/nWW2\">http:\/\/mng.bz\/nWW2<\/a> \u4e0a\u9605\u8bfb\u6709\u5173 CSP \u7684\u4fe1\u606f\u3002\u6211\u6709\u4e00\u4e2a\u5f00\u6e90\u5e93\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528\u5b83\u5c06 CSP \u96c6\u6210\u5230 NuGet \u4e0a\u63d0\u4f9b\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\uff0c\u7f51\u5740\u4e3a <a href=\"http:\/\/mng.bz\/vnn4\">http:\/\/mng.bz\/vnn4<\/a>\u3002<\/p>\n<p>The vulnerability here is due to rendering the user input in an unsafe way. If the data isn\u2019t encoded to make it safe before it\u2019s rendered, you could open your users to attack. By default, Razor protects against XSS attacks by HTML- encoding any data written using Tag Helpers, HTML Helpers, or the @ syntax. So generally you should be safe, as you saw in chapter 17.<br \/>\n\u6b64\u5904\u7684\u6f0f\u6d1e\u662f\u7531\u4e8e\u4ee5\u4e0d\u5b89\u5168\u7684\u65b9\u5f0f\u5448\u73b0\u7528\u6237\u8f93\u5165\u3002\u5982\u679c\u6570\u636e\u5728\u5448\u73b0\u4e4b\u524d\u6ca1\u6709\u8fdb\u884c\u7f16\u7801\u4ee5\u786e\u4fdd\u5176\u5b89\u5168\uff0c\u5219\u53ef\u80fd\u4f1a\u4f7f\u7528\u6237\u53d7\u5230\u653b\u51fb\u3002\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0cRazor \u901a\u8fc7\u5bf9\u4f7f\u7528\u6807\u8bb0\u5e2e\u52a9\u7a0b\u5e8f\u3001HTML \u5e2e\u52a9\u7a0b\u5e8f\u6216 @ \u8bed\u6cd5\u5199\u5165\u7684\u4efb\u4f55\u6570\u636e\u8fdb\u884c HTML \u7f16\u7801\u6765\u9632\u6b62 XSS \u653b\u51fb\u3002\u6240\u4ee5\u4e00\u822c\u6765\u8bf4\u4f60\u5e94\u8be5\u662f\u5b89\u5168\u7684\uff0c\u5c31\u50cf\u4f60\u5728\u7b2c 17 \u7ae0\u4e2d\u770b\u5230\u7684\u90a3\u6837\u3002<\/p>\n<p>Using @Html.Raw() is where the danger lies: if the HTML you\u2019re rendering contains user input (even indirectly), you could have an XSS vulnerability. By rendering the user input with @ instead, the content is encoded before it\u2019s written to the output, as shown in \ufb01gure 29.2.<br \/>\n\u4f7f\u7528 @Html.Raw\uff08\uff09 \u662f\u5371\u9669\u6240\u5728\uff1a\u5982\u679c\u60a8\u6e32\u67d3\u7684 HTML \u5305\u542b\u7528\u6237\u8f93\u5165\uff08\u5373\u4f7f\u662f\u95f4\u63a5\u7684\uff09\uff0c\u5219\u53ef\u80fd\u5b58\u5728 XSS \u6f0f\u6d1e\u3002\u901a\u8fc7\u4f7f\u7528 @ \u6765\u5448\u73b0\u7528\u6237\u8f93\u5165\uff0c\u5185\u5bb9\u5728\u5199\u5165\u8f93\u51fa\u4e4b\u524d\u8fdb\u884c\u7f16\u7801\uff0c\u5982\u56fe 29.2 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/2902.jpg\" alt=\"alt text\" \/><\/p>\n<p>Figure 29.2 Protecting against XSS attacks by HTML- encoding user input using @ in Razor templates. The <code>&lt;script&gt;<\/code> tag is encoded so that it is no longer rendered as HTML and can\u2019t be used to compromise your app.<br \/>\n\u56fe 29.2 \u5728 Razor \u6a21\u677f\u4e2d\u4f7f\u7528 @ \u5bf9\u7528\u6237\u8f93\u5165\u8fdb\u884c HTML \u7f16\u7801\u6765\u9632\u8303 XSS \u653b\u51fb\u3002\u8be5 <code>&lt;script&gt;<\/code>\u6807\u8bb0\u7ecf\u8fc7\u7f16\u7801\uff0c\u56e0\u6b64\u5b83\u4e0d\u518d\u5448\u73b0\u4e3a HTML\uff0c\u4e5f\u4e0d\u80fd\u7528\u4e8e\u5371\u5bb3\u60a8\u7684\u5e94\u7528\u3002<\/p>\n<p>This example demonstrates using HTML encoding to prevent elements being directly added to the HTML Document Object Model (DOM), but it\u2019s not the only case you have to think about. If you\u2019re passing untrusted data to JavaScript or using untrusted data in URL query values, you must make sure to encode the data correctly.<br \/>\n\u6b64\u793a\u4f8b\u6f14\u793a\u4e86\u5982\u4f55\u4f7f\u7528 HTML \u7f16\u7801\u6765\u9632\u6b62\u5143\u7d20\u88ab\u76f4\u63a5\u6dfb\u52a0\u5230 HTML \u6587\u6863\u5bf9\u8c61\u6a21\u578b \uff08DOM\uff09 \u4e2d\uff0c\u4f46\u8fd9\u5e76\u4e0d\u662f\u60a8\u5fc5\u987b\u8003\u8651\u7684\u552f\u4e00\u60c5\u51b5\u3002\u5982\u679c\u8981\u5c06\u4e0d\u53d7\u4fe1\u4efb\u7684\u6570\u636e\u4f20\u9012\u7ed9 JavaScript \u6216\u5728 URL \u67e5\u8be2\u503c\u4e2d\u4f7f\u7528\u4e0d\u53d7\u4fe1\u4efb\u7684\u6570\u636e\uff0c\u5219\u5fc5\u987b\u786e\u4fdd\u6b63\u786e\u7f16\u7801\u6570\u636e\u3002<\/p>\n<p>A common scenario is when you\u2019re using JavaScript with Razor Pages, and you want to pass a value from the server to the client. If you use the standard @ symbol to render the data to the page, the output will be HTML-encoded.<br \/>\n\u4e00\u79cd\u5e38\u89c1\u60c5\u51b5\u662f\uff0c\u5c06 JavaScript \u4e0e Razor Pages \u914d\u5408\u4f7f\u7528\uff0c\u5e76\u4e14\u60f3\u8981\u5c06\u503c\u4ece\u670d\u52a1\u5668\u4f20\u9012\u5230\u5ba2\u6237\u7aef\u3002\u5982\u679c\u4f7f\u7528\u6807\u51c6 @ \u7b26\u53f7\u5c06\u6570\u636e\u5448\u73b0\u5230\u9875\u9762\uff0c\u5219\u8f93\u51fa\u5c06\u91c7\u7528 HTML \u7f16\u7801\u3002<\/p>\n<p>Unfortunately, if you HTML-encode a string and inject it directly into JavaScript, you probably won\u2019t get what you expect.<br \/>\n\u4e0d\u5e78\u7684\u662f\uff0c\u5982\u679c\u4f60\u5bf9\u5b57\u7b26\u4e32\u8fdb\u884c HTML \u7f16\u7801\u5e76\u5c06\u5176\u76f4\u63a5\u6ce8\u5165\u5230 JavaScript \u4e2d\uff0c\u4f60\u53ef\u80fd\u4e0d\u4f1a\u5f97\u5230\u4f60\u6240\u671f\u671b\u7684\u7ed3\u679c\u3002<\/p>\n<p>For example, if you have a variable in your Razor \ufb01le called name, and you want to make it available in JavaScript, you might be tempted to use something like this:<br \/>\n\u4f8b\u5982\uff0c\u5982\u679c\u60a8\u7684 Razor \u6587\u4ef6\u4e2d\u6709\u4e00\u4e2a\u540d\u4e3a name \u7684\u53d8\u91cf\uff0c\u5e76\u4e14\u60a8\u5e0c\u671b\u5728 JavaScript \u4e2d\u4f7f\u5176\u53ef\u7528\uff0c\u60a8\u53ef\u80fd\u4f1a\u60f3\u4f7f\u7528\u5982\u4e0b\u5185\u5bb9\uff1a<\/p>\n<pre><code>&lt;script&gt;var name = &#039;@name&#039;&lt;\/script&gt;<\/code><\/pre>\n<p>If the name contains special characters, Razor will encode them using HTML encoding, which probably isn\u2019t what you want in this JavaScript context. For example, if name was Arnold &quot;Arnie&quot; Schwarzenegger, rendering it as you did previously would give this:<br \/>\n\u5982\u679c\u540d\u79f0\u5305\u542b\u7279\u6b8a\u5b57\u7b26\uff0cRazor \u5c06\u4f7f\u7528 HTML \u7f16\u7801\u5bf9\u5176\u8fdb\u884c\u7f16\u7801\uff0c\u8fd9\u53ef\u80fd\u4e0d\u662f\u4f60\u5728\u6b64 JavaScript \u4e0a\u4e0b\u6587\u4e2d\u60f3\u8981\u7684\u3002\u4f8b\u5982\uff0c\u5982\u679c name \u662f Arnold \u201cArnie\u201d Schwarzenegger\uff0c\u5219\u50cf\u4ee5\u524d\u4e00\u6837\u5448\u73b0\u5b83\u5c06\u5f97\u5230\u4ee5\u4e0b\u7ed3\u679c\uff1a<\/p>\n<pre><code>&lt;script&gt;var name = &#039;Arnold &quot;Arnie&quot; Schwarzenegger&#039;;&lt;\/script&gt;<\/code><\/pre>\n<p>Note that the double quotation marks (&quot;) have been HTML- encoded to &quot;. If you use this value in JavaScript directly, expecting it to be a safe encoded value, it\u2019s going to look wrong, as shown in \ufb01gure 29.3.<br \/>\n\u8bf7\u6ce8\u610f\uff0c\u53cc\u5f15\u53f7 \uff08\u201c\uff09 \u5df2 HTML \u7f16\u7801\u4e3a \u201d.\u5982\u679c\u4f60\u76f4\u63a5\u5728 JavaScript \u4e2d\u4f7f\u7528\u8fd9\u4e2a\u503c\uff0c\u671f\u671b\u5b83\u662f\u4e00\u4e2a\u5b89\u5168\u7684\u7f16\u7801\u503c\uff0c\u5b83\u770b\u8d77\u6765\u4f1a\u51fa\u9519\uff0c\u5982\u56fe 29.3 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/2903.jpg\" alt=\"alt text\" \/><\/p>\n<p>Figure 29.3 Comparison of alerts when using JavaScript encoding compared with HTML encoding<br \/>\n\u56fe 29.3 \u4f7f\u7528 JavaScript \u7f16\u7801\u4e0e HTML \u7f16\u7801\u65f6\u7684\u8b66\u62a5\u6bd4\u8f83<\/p>\n<p>Instead, you should encode the variable using JavaScript encoding so that the double-quote character is rendered as a safe Unicode character, \\u0022. You can achieve this by injecting a JavaScriptEncoder into the view and calling Encode() on the name variable:<br \/>\n\u76f8\u53cd\uff0c\u60a8\u5e94\u8be5\u4f7f\u7528 JavaScript \u7f16\u7801\u5bf9\u53d8\u91cf\u8fdb\u884c\u7f16\u7801\uff0c\u4ee5\u4fbf\u5c06\u53cc\u5f15\u53f7\u5b57\u7b26\u5448\u73b0\u4e3a\u5b89\u5168\u7684 Unicode \u5b57\u7b26 \\u0022\u3002\u60a8\u53ef\u4ee5\u901a\u8fc7\u5c06 JavaScriptEncoder \u6ce8\u5165\u89c6\u56fe\u5e76\u5728 name \u53d8\u91cf\u4e0a\u8c03\u7528 Encode\uff08\uff09 \u6765\u5b9e\u73b0\u8fd9\u4e00\u70b9\uff1a<\/p>\n<pre><code>@inject System.Text.Encodings.Web.JavaScriptEncoder encoder;\n&lt;script&gt;var name = &#039;@encoder.Encode(name)&#039;&lt;\/script&gt;<\/code><\/pre>\n<p>To avoid having to remember to use JavaScript encoding, I recommend that you don\u2019t write values into JavaScript like this. Instead, write the value to an HTML element\u2019s attributes, and then read that into the JavaScript variable later, as shown in the following listing. That prevents the need for the JavaScript encoder entirely.<br \/>\n\u4e3a\u907f\u514d\u8bb0\u4f4f\u4f7f\u7528 JavaScript \u7f16\u7801\uff0c\u6211\u5efa\u8bae\u60a8\u4e0d\u8981\u50cf\u8fd9\u6837\u5c06\u503c\u5199\u5165 JavaScript\u3002\u76f8\u53cd\uff0c\u5c06\u503c\u5199\u5165 HTML \u5143\u7d20\u7684\u5c5e\u6027\uff0c\u7136\u540e\u7a0d\u540e\u5c06\u5176\u8bfb\u53d6\u5230 JavaScript \u53d8\u91cf\u4e2d\uff0c\u5982\u4e0b\u9762\u7684\u6e05\u5355\u6240\u793a\u3002\u8fd9\u5b8c\u5168\u4e0d\u9700\u8981 JavaScript \u7f16\u7801\u5668\u3002<\/p>\n<p>Listing 29.1 Passing values to JavaScript by writing them to HTML attributes<br \/>\n\u6e05\u5355 29.1 \u901a\u8fc7\u5c06\u503c\u5199\u5165 HTML \u5c5e\u6027\u6765\u5c06\u503c\u4f20\u9012\u7ed9 JavaScript<\/p>\n<pre><code>&lt;div id=&quot;data&quot; data-name=&quot;@name&quot;&gt;&lt;\/div&gt;\n&lt;script&gt; \u2776\nvar ele = document.getElementById(&#039;data&#039;); \u2777\nvar name = ele.getAttribute(&#039;data-name&#039;); \u2778\n&lt;\/script&gt;<\/code><\/pre>\n<p>\u2776 Write the value you want in JavaScript to a data-* attribute. This HTML-encodes the data.<br \/>\n\u5728 JavaScript \u4e2d\u5c06\u4f60\u60f3\u8981\u7684\u503c\u5199\u5165 data- \u5c5e\u6027\u3002\u6b64 HTML \u5bf9\u6570\u636e\u8fdb\u884c\u7f16\u7801\u3002<\/p>\n<p>\u2777 Gets a reference to the HTML element<br \/>\n\u83b7\u53d6\u5bf9 HTML \u5143\u7d20\u7684\u5f15\u7528<\/p>\n<p>\u2778 Reads the data-* attribute into JavaScript, which converts it to JavaScript encoding<br \/>\n\u5c06 data- \u5c5e\u6027\u8bfb\u53d6\u5230 JavaScript \u4e2d\uff0c\u4ece\u800c\u5c06\u5176\u8f6c\u6362\u4e3a JavaScript \u7f16\u7801<\/p>\n<p>XSS attacks are still common, and it\u2019s easy to expose yourself to them whenever you allow users to input data. Validation of the incoming data can help sometimes, but it\u2019s often a tricky problem. For example, a naive name validator might require that you use only letters, which would prevent most attacks. Unfortunately, that doesn\u2019t account for users with hyphens or apostrophes in their name, let alone users with non-Western names. People get (understandably) upset when you tell them that their name is invalid, so be wary of this approach!<br \/>\nXSS \u653b\u51fb\u4ecd\u7136\u5f88\u5e38\u89c1\uff0c\u53ea\u8981\u60a8\u5141\u8bb8\u7528\u6237\u8f93\u5165\u6570\u636e\uff0c\u5c31\u5f88\u5bb9\u6613\u5c06\u81ea\u5df1\u66b4\u9732\u5728\u5b83\u4eec\u9762\u524d\u3002\u9a8c\u8bc1\u4f20\u5165\u6570\u636e\u6709\u65f6\u4f1a\u6709\u6240\u5e2e\u52a9\uff0c\u4f46\u8fd9\u901a\u5e38\u662f\u4e00\u4e2a\u68d8\u624b\u7684\u95ee\u9898\u3002\u4f8b\u5982\uff0c\u4e00\u4e2a naive name validator \u53ef\u80fd\u8981\u6c42\u60a8\u53ea\u4f7f\u7528\u5b57\u6bcd\uff0c\u8fd9\u6837\u53ef\u4ee5\u9632\u6b62\u5927\u591a\u6570\u653b\u51fb\u3002\u4e0d\u5e78\u7684\u662f\uff0c\u8fd9\u5e76\u672a\u8003\u8651\u540d\u79f0\u4e2d\u5305\u542b\u8fde\u5b57\u7b26\u6216\u6487\u53f7\u7684\u7528\u6237\uff0c\u66f4\u4e0d\u7528\u8bf4\u5177\u6709\u975e\u897f\u65b9\u540d\u79f0\u7684\u7528\u6237\u4e86\u3002\u5f53\u4f60\u544a\u8bc9\u4ed6\u4eec\u4ed6\u4eec\u7684\u540d\u5b57\u65e0\u6548\u65f6\uff0c\u4eba\u4eec\u4f1a\uff08\u53ef\u4ee5\u7406\u89e3\u5730\uff09\u4e0d\u5b89\uff0c\u6240\u4ee5\u8981\u8b66\u60d5\u8fd9\u79cd\u505a\u6cd5\uff01<\/p>\n<p>Whether or not you use strict validation, you should always encode the data when you render it to the page. Think carefully whenever you \ufb01nd yourself writing @Html.Raw(). Is there any way, no matter how contrived, for a user to get malicious data into that \ufb01eld? If so, you\u2019ll need to \ufb01nd another way to display the data.<br \/>\n\u65e0\u8bba\u662f\u5426\u4f7f\u7528\u4e25\u683c\u9a8c\u8bc1\uff0c\u5728\u5c06\u6570\u636e\u5448\u73b0\u5230\u9875\u9762\u65f6\uff0c\u90fd\u5e94\u59cb\u7ec8\u5bf9\u6570\u636e\u8fdb\u884c\u7f16\u7801\u3002\u6bcf\u5f53\u60a8\u53d1\u73b0\u81ea\u5df1\u7f16\u5199 @Html.Raw\uff08\uff09 \u65f6\uff0c\u8bf7\u4ed4\u7ec6\u8003\u8651\u3002\u65e0\u8bba\u591a\u4e48\u4eba\u4e3a\uff0c\u7528\u6237\u662f\u5426\u6709\u4efb\u4f55\u65b9\u6cd5\u53ef\u4ee5\u5c06\u6076\u610f\u6570\u636e\u5bfc\u5165\u8be5\u5b57\u6bb5\uff1f\u5982\u679c\u662f\u8fd9\u6837\uff0c\u60a8\u5c06\u9700\u8981\u627e\u5230\u53e6\u4e00\u79cd\u663e\u793a\u6570\u636e\u7684\u65b9\u6cd5\u3002<\/p>\n<p>XSS vulnerabilities allow attackers to execute JavaScript on a user\u2019s browser. The next vulnerability we\u2019re going to consider lets them make requests to your API as though they\u2019re a di\ufb00erent logged-in user, even when the user isn\u2019t using your app. Scared? I hope so!\u200c<br \/>\nXSS \u6f0f\u6d1e\u5141\u8bb8\u653b\u51fb\u8005\u5728\u7528\u6237\u7684\u6d4f\u89c8\u5668\u4e0a\u6267\u884c JavaScript\u3002\u6211\u4eec\u5c06\u8981\u8003\u8651\u7684\u4e0b\u4e00\u4e2a\u6f0f\u6d1e\u5141\u8bb8\u4ed6\u4eec\u5411\u60a8\u7684 API \u53d1\u51fa\u8bf7\u6c42\uff0c\u5c31\u597d\u50cf\u4ed6\u4eec\u662f\u4e0d\u540c\u7684\u767b\u5f55\u7528\u6237\u4e00\u6837\uff0c\u5373\u4f7f\u8be5\u7528\u6237\u6ca1\u6709\u4f7f\u7528\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u3002\u5bb3\u6015\u5417\uff1f\u5e0c\u671b\u5982\u6b64\uff01<\/p>\n<h2>29.2 Protecting from cross-site request forgery (CSRF) attacks\u200c<\/h2>\n<p>29.2 \u9632\u6b62\u8de8\u7ad9\u70b9\u8bf7\u6c42\u4f2a\u9020 \uff08CSRF\uff09 \u653b\u51fb<\/p>\n<p>In this section you\u2019ll learn about CSRF attacks, how attackers can use them to impersonate a user on your site, and how to protect against them using antiforgery tokens. Razor Pages protects you from these attacks by default, but you can disable these veri\ufb01cations, so it\u2019s important to understand the implications of doing so.<br \/>\n\u5728\u672c\u8282\u4e2d\uff0c\u60a8\u5c06\u4e86\u89e3 CSRF \u653b\u51fb\u3001\u653b\u51fb\u8005\u5982\u4f55\u4f7f\u7528\u5b83\u4eec\u6765\u5192\u5145\u60a8\u7f51\u7ad9\u4e0a\u7684\u7528\u6237\uff0c\u4ee5\u53ca\u5982\u4f55\u4f7f\u7528\u9632\u4f2a\u4ee4\u724c\u6765\u9632\u8303\u5b83\u4eec\u3002\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0cRazor Pages \u4f1a\u4fdd\u62a4\u60a8\u514d\u53d7\u8fd9\u4e9b\u653b\u51fb\uff0c\u4f46\u60a8\u53ef\u4ee5\u7981\u7528\u8fd9\u4e9b\u9a8c\u8bc1\uff0c\u56e0\u6b64\u8bf7\u52a1\u5fc5\u4e86\u89e3\u8fd9\u6837\u505a\u7684\u542b\u4e49\u3002<\/p>\n<p>CSRF attacks can be a problem for websites or APIs that use cookies for authentication. A CSRF attack involves a malicious website making an authenticated request to your API on behalf of the user, without the user\u2019s initiating the request. In this section we\u2019ll explore how these attacks work and how you can mitigate them with antiforgery tokens.<br \/>\n\u5bf9\u4e8e\u4f7f\u7528 cookie \u8fdb\u884c\u8eab\u4efd\u9a8c\u8bc1\u7684\u7f51\u7ad9\u6216 API \u6765\u8bf4\uff0cCSRF \u653b\u51fb\u53ef\u80fd\u662f\u4e00\u4e2a\u95ee\u9898\u3002CSRF \u653b\u51fb\u6d89\u53ca\u6076\u610f\u7f51\u7ad9\u4ee3\u8868\u7528\u6237\u5411\u60a8\u7684 API \u53d1\u51fa\u7ecf\u8fc7\u8eab\u4efd\u9a8c\u8bc1\u7684\u8bf7\u6c42\uff0c\u800c\u65e0\u9700\u7528\u6237\u53d1\u8d77\u8bf7\u6c42\u3002\u5728\u672c\u8282\u4e2d\uff0c\u6211\u4eec\u5c06\u63a2\u8ba8\u8fd9\u4e9b\u653b\u51fb\u7684\u5de5\u4f5c\u539f\u7406\uff0c\u4ee5\u53ca\u5982\u4f55\u4f7f\u7528\u9632\u4f2a\u4ee4\u724c\u6765\u7f13\u89e3\u5b83\u4eec\u3002<\/p>\n<p>The canonical example of this attack is a bank transfer\/withdrawal. Imagine you have a banking application that stores authentication tokens in a cookie, as is common (especially in traditional server-side rendered applications).Browsers automatically send the cookies associated with a domain with every request so the app knows whether a user is authenticated.<br \/>\n\u8fd9\u79cd\u653b\u51fb\u7684\u5178\u578b\u793a\u4f8b\u662f\u94f6\u884c\u8f6c\u8d26\/\u53d6\u6b3e\u3002\u5047\u8bbe\u60a8\u6709\u4e00\u4e2a\u94f6\u884c\u5e94\u7528\u7a0b\u5e8f\uff0c\u5b83\u5c06\u8eab\u4efd\u9a8c\u8bc1\u4ee4\u724c\u5b58\u50a8\u5728 Cookie \u4e2d\uff0c\u8fd9\u5f88\u5e38\u89c1\uff08\u5c24\u5176\u662f\u5728\u4f20\u7edf\u7684\u670d\u52a1\u5668\u7aef\u5448\u73b0\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\uff09\u3002\u6d4f\u89c8\u5668\u4f1a\u81ea\u52a8\u5c06\u4e0e\u57df\u5173\u8054\u7684 Cookie \u4e0e\u6bcf\u4e2a\u8bf7\u6c42\u4e00\u8d77\u53d1\u9001\uff0c\u4ee5\u4fbf\u5e94\u7528\u7a0b\u5e8f\u77e5\u9053\u7528\u6237\u662f\u5426\u7ecf\u8fc7\u8eab\u4efd\u9a8c\u8bc1\u3002<\/p>\n<p>Now imagine your application has a page that lets a user transfer funds from their account to another account using a POST request to the Balance Razor Page. You have to be logged in to access the form (you\u2019ve protected the Razor Page with the [Authorize] attribute or global authorization requirements), but otherwise you post a form that says how much you want to transfer and where you want to transfer it. Seems simple enough?\u200c<br \/>\n\u73b0\u5728\uff0c\u5047\u8bbe\u4f60\u7684\u5e94\u7528\u7a0b\u5e8f\u6709\u4e00\u4e2a\u9875\u9762\uff0c\u8be5\u9875\u9762\u5141\u8bb8\u7528\u6237\u4f7f\u7528\u5bf9 Balance Razor \u9875\u9762\u7684 POST \u8bf7\u6c42\u5c06\u8d44\u91d1\u4ece\u5176\u5e10\u6237\u8f6c\u79fb\u5230\u53e6\u4e00\u4e2a\u5e10\u6237\u3002\u60a8\u5fc5\u987b\u767b\u5f55\u624d\u80fd\u8bbf\u95ee\u8be5\u8868\u5355\uff08\u60a8\u5df2\u4f7f\u7528 [Authorize] \u5c5e\u6027\u6216\u5168\u5c40\u6388\u6743\u8981\u6c42\u4fdd\u62a4\u4e86 Razor \u9875\u9762\uff09\uff0c\u4f46\u9664\u6b64\u4e4b\u5916\uff0c\u60a8\u9700\u8981\u53d1\u5e03\u4e00\u4e2a\u8868\u5355\uff0c\u8bf4\u660e\u60a8\u8981\u8f6c\u79fb\u7684\u91d1\u989d\u4ee5\u53ca\u8981\u8f6c\u79fb\u7684\u4f4d\u7f6e\u3002\u770b\u8d77\u6765\u5f88\u7b80\u5355\uff1f<\/p>\n<p>Suppose that a user visits your site, logs in, and performs a transaction. Then they visit a second website that the attacker has control of. The attacker has embedded a form in their website that performs a POST to your bank\u2019s website, identical to the transfer-funds form on your banking website. This form does something malicious, such as transfer all the user\u2019s funds to the attacker, as shown in \ufb01gure 29.4.<br \/>\n\u5047\u8bbe\u7528\u6237\u8bbf\u95ee\u60a8\u7684\u7f51\u7ad9\u3001\u767b\u5f55\u5e76\u6267\u884c\u4e8b\u52a1\u3002\u7136\u540e\uff0c\u4ed6\u4eec\u8bbf\u95ee\u653b\u51fb\u8005\u53ef\u4ee5\u63a7\u5236\u7684\u7b2c\u4e8c\u4e2a\u7f51\u7ad9\u3002\u653b\u51fb\u8005\u5728\u5176\u7f51\u7ad9\u4e2d\u5d4c\u5165\u4e86\u4e00\u4e2a\u8868\u5355\uff0c\u8be5\u8868\u5355\u4f1a\u5411\u60a8\u7684\u94f6\u884c\u7f51\u7ad9\u6267\u884c POST\uff0c\u8be5\u8868\u5355\u4e0e\u60a8\u7684\u94f6\u884c\u7f51\u7ad9\u4e0a\u7684\u8f6c\u8d26\u8d44\u91d1\u8868\u5355\u76f8\u540c\u3002\u8fd9\u79cd\u5f62\u5f0f\u4f1a\u505a\u4e00\u4e9b\u6076\u610f\u7684\u4e8b\u60c5\uff0c\u6bd4\u5982\u628a\u7528\u6237\u7684\u6240\u6709\u8d44\u91d1\u90fd\u8f6c\u79fb\u7ed9\u653b\u51fb\u8005\uff0c\u5982\u56fe 29.4 \u6240\u793a\u3002<\/p>\n<p>Browsers automatically send the cookies for the application when the page does a full form post, and the banking app has no way of knowing that this is a malicious request. The unsuspecting user has given all their money to the attacker!<br \/>\n\u5f53\u9875\u9762\u6267\u884c\u5b8c\u6574\u8868\u5355\u53d1\u5e03\u65f6\uff0c\u6d4f\u89c8\u5668\u4f1a\u81ea\u52a8\u53d1\u9001\u5e94\u7528\u7a0b\u5e8f\u7684 Cookie\uff0c\u800c\u94f6\u884c\u5e94\u7528\u7a0b\u5e8f\u65e0\u6cd5\u77e5\u9053\u8fd9\u662f\u6076\u610f\u8bf7\u6c42\u3002\u6beb\u65e0\u6212\u5fc3\u7684\u7528\u6237\u5df2\u7ecf\u628a\u4ed6\u4eec\u6240\u6709\u7684\u94b1\u90fd\u7ed9\u4e86\u653b\u51fb\u8005\uff01<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/2904.jpg\" alt=\"alt text\" \/><\/p>\n<p>Figure 29.4 A CSRF attack occurs when a logged-in user visits a malicious site. The malicious site crafts a form that matches one on your app and POSTs it to your app. The browser sends the authentication cookie automatically, so your app sees the request as a valid request from the user.<br \/>\n\u56fe 29.4 \u5f53\u767b\u5f55\u7528\u6237\u8bbf\u95ee\u6076\u610f\u7ad9\u70b9\u65f6\uff0c\u4f1a\u53d1\u751f CSRF \u653b\u51fb\u3002\u6076\u610f\u7f51\u7ad9\u4f1a\u5236\u4f5c\u4e00\u4e2a\u4e0e\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u5339\u914d\u7684\u8868\u5355\uff0c\u5e76\u5c06\u5176 POST \u5230\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u3002\u6d4f\u89c8\u5668\u4f1a\u81ea\u52a8\u53d1\u9001\u8eab\u4efd\u9a8c\u8bc1 Cookie\uff0c\u56e0\u6b64\u60a8\u7684\u5e94\u7528\u4f1a\u5c06\u8be5\u8bf7\u6c42\u89c6\u4e3a\u6765\u81ea\u7528\u6237\u7684\u6709\u6548\u8bf7\u6c42\u3002<\/p>\n<p>The vulnerability here revolves around the fact that browsers automatically send cookies when a page is requested (using a GET request) or a form is POSTed. There\u2019s no di\ufb00erence between a legitimate POST of the form in your banking app and the attacker\u2019s malicious POST. Unfortunately, this behavior is baked into the web; it\u2019s what allows you to navigate websites seamlessly after initially logging in.<br \/>\n\u6b64\u5904\u7684\u6f0f\u6d1e\u56f4\u7ed5\u4ee5\u4e0b\u4e8b\u5b9e\u5c55\u5f00\uff1a\u6d4f\u89c8\u5668\u5728\u8bf7\u6c42\u9875\u9762\uff08\u4f7f\u7528 GET \u8bf7\u6c42\uff09\u6216\u53d1\u5e03\u8868\u5355\u65f6\u81ea\u52a8\u53d1\u9001 Cookie\u3002\u60a8\u7684\u94f6\u884c\u5e94\u7528\u7a0b\u5e8f\u4e2d\u5f62\u5f0f\u7684\u5408\u6cd5 POST \u4e0e\u653b\u51fb\u8005\u7684\u6076\u610f POST \u4e4b\u95f4\u6ca1\u6709\u533a\u522b\u3002\u4e0d\u5e78\u7684\u662f\uff0c\u8fd9\u79cd\u884c\u4e3a\u5df2\u7ecf\u878d\u5165\u4e86 Web;\u5b83\u5141\u8bb8\u60a8\u5728\u521d\u59cb\u767b\u5f55\u540e\u65e0\u7f1d\u6d4f\u89c8\u7f51\u7ad9\u3002<\/p>\n<p><b>TIP<\/b>  Browsers have additional protections to prevent cookies being sent in this situation, called SameSite cookies. By default, most browsers use SameSite=Lax, which prevents this vulnerable behavior. You can read about SameSite cookies and how to work with them in ASP.NET Core at <a href=\"http:\/\/mng.bz\/4DDj\">http:\/\/mng.bz\/4DDj<\/a>.<br \/>\n\u63d0\u793a:\u6d4f\u89c8\u5668\u5177\u6709\u989d\u5916\u7684\u4fdd\u62a4\u63aa\u65bd\u6765\u9632\u6b62\u5728\u8fd9\u79cd\u60c5\u51b5\u4e0b\u53d1\u9001 Cookie\uff0c\u79f0\u4e3a SameSite Cookie\u3002\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u5927\u591a\u6570\u6d4f\u89c8\u5668\u4f7f\u7528 SameSite=Lax\uff0c\u8fd9\u53ef\u4ee5\u9632\u6b62\u8fd9\u79cd\u6613\u53d7\u653b\u51fb\u7684\u884c\u4e3a\u3002\u60a8\u53ef\u4ee5\u5728 <a href=\"http:\/\/mng.bz\/4DDj\">http:\/\/mng.bz\/4DDj<\/a> \u9605\u8bfb\u6709\u5173 SameSite Cookie \u4ee5\u53ca\u5982\u4f55\u5728 ASP.NET Core \u4e2d\u4f7f\u7528\u5b83\u4eec\u7684\u4fe1\u606f\u3002<\/p>\n<p>A common solution to this CSRF attack is the synchronizer token pattern, which uses user-speci\ufb01c, unique antiforgery tokens to enforce a di\ufb00erence between a legitimate POST and a forged POST from an attacker. One token is stored in a cookie, and another is added to the form you wish to protect. Your app generates the tokens at runtime based on the current logged-in user, so there\u2019s no way for an attacker to create one for their forged form.<br \/>\n\u8fd9\u79cd CSRF \u653b\u51fb\u7684\u5e38\u89c1\u89e3\u51b3\u65b9\u6848\u662f\u540c\u6b65\u5668\u4ee4\u724c\u6a21\u5f0f\uff0c\u5b83\u4f7f\u7528\u7279\u5b9a\u4e8e\u7528\u6237\u7684\u552f\u4e00\u9632\u4f2a\u4ee4\u724c\u6765\u5f3a\u5236\u533a\u5206\u6765\u81ea\u653b\u51fb\u8005\u7684\u5408\u6cd5 POST \u548c\u4f2a\u9020\u7684 POST\u3002\u4e00\u4e2a\u4ee4\u724c\u5b58\u50a8\u5728 Cookie \u4e2d\uff0c\u53e6\u4e00\u4e2a\u4ee4\u724c\u5c06\u6dfb\u52a0\u5230\u60a8\u8981\u4fdd\u62a4\u7684\u8868\u5355\u4e2d\u3002\u60a8\u7684\u5e94\u7528\u5728\u8fd0\u884c\u65f6\u6839\u636e\u5f53\u524d\u767b\u5f55\u7528\u6237\u751f\u6210\u4ee4\u724c\uff0c\u56e0\u6b64\u653b\u51fb\u8005\u65e0\u6cd5\u4e3a\u5176\u4f2a\u9020\u8868\u5355\u521b\u5efa\u4ee4\u724c\u3002<\/p>\n<p><b>TIP<\/b>  The \u201cCross-Site Request Forgery Prevention Cheat Sheet\u201d article on the OWASP site (<a href=\"http:\/\/mng.bz\/5jRa\">http:\/\/mng.bz\/5jRa<\/a>) has a thorough discussion of the CSRF vulnerability, including the synchronizer token pattern.<br \/>\n\u63d0\u793a:OWASP \u7ad9\u70b9 \uff08<a href=\"http:\/\/mng.bz\/5jRa\">http:\/\/mng.bz\/5jRa<\/a>\uff09 \u4e0a\u7684\u201c\u8de8\u7ad9\u70b9\u8bf7\u6c42\u4f2a\u9020\u9884\u9632\u5907\u5fd8\u5355\u201d\u4e00\u6587\u5bf9 CSRF \u6f0f\u6d1e\u8fdb\u884c\u4e86\u6df1\u5165\u8ba8\u8bba\uff0c\u5305\u62ec\u540c\u6b65\u5668\u4ee4\u724c\u6a21\u5f0f\u3002<\/p>\n<p>When the Balance Razor Page receives a form POST, it compares the value in the form with the value in the cookie. If either value is missing or the values don\u2019t match, the request is rejected. If an attacker creates a POST, the browser posts the cookie token as usual, but there won\u2019t be a token in the form itself or the token won\u2019t be valid. The Razor Page rejects the request, protecting from the CSRF attack, as in \ufb01gure 29.5.<br \/>\n\u5f53 Balance Razor \u9875\u9762\u6536\u5230\u8868\u5355 POST \u65f6\uff0c\u5b83\u4f1a\u5c06\u8868\u5355\u4e2d\u7684\u503c\u4e0e Cookie \u4e2d\u7684\u503c\u8fdb\u884c\u6bd4\u8f83\u3002\u5982\u679c\u7f3a\u5c11\u4efb\u4e00\u503c\u6216\u503c\u4e0d\u5339\u914d\uff0c\u5219\u8bf7\u6c42\u5c06\u88ab\u62d2\u7edd\u3002\u5982\u679c\u653b\u51fb\u8005\u521b\u5efa POST\uff0c\u6d4f\u89c8\u5668\u4f1a\u7167\u5e38\u53d1\u5e03 cookie \u4ee4\u724c\uff0c\u4f46\u8868\u5355\u672c\u8eab\u4e0d\u4f1a\u6709\u4ee4\u724c\uff0c\u6216\u8005\u4ee4\u724c\u65e0\u6548\u3002Razor Page \u62d2\u7edd\u8bf7\u6c42\uff0c\u9632\u6b62 CSRF \u653b\u51fb\uff0c\u5982\u56fe 29.5 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/2905.jpg\" alt=\"alt text\" \/><\/p>\n<p>Figure 29.5 Protecting against a CSRF attack using antiforgery tokens. The browser automatically forwards the cookie token, but the malicious site can\u2019t read it and so can\u2019t include a token in the form.The app rejects the malicious request because the tokens don\u2019t match.<br \/>\n\u56fe 29.5 \u4f7f\u7528\u9632\u4f2a\u4ee4\u724c\u9632\u8303 CSRF \u653b\u51fb\u3002\u6d4f\u89c8\u5668\u4f1a\u81ea\u52a8\u8f6c\u53d1 Cookie \u4ee4\u724c\uff0c\u4f46\u6076\u610f\u7ad9\u70b9\u65e0\u6cd5\u8bfb\u53d6\u5b83\uff0c\u56e0\u6b64\u65e0\u6cd5\u5728\u8868\u5355\u4e2d\u5305\u542b\u4ee4\u724c\u3002\u5e94\u7528\u7a0b\u5e8f\u62d2\u7edd\u6076\u610f\u8bf7\u6c42\uff0c\u56e0\u4e3a\u4ee4\u724c\u4e0d\u5339\u914d\u3002<\/p>\n<p>The good news is that Razor Pages automatically protects you against CSRF attacks. The Form Tag Helper automatically sets an antiforgery token cookie and renders the token to a hidden \ufb01eld called _RequestVerificationToken for every <code>&lt;form&gt;<\/code> element in your app (unless you speci\ufb01cally disable them). For example, take this simple Razor template that posts back to the same Razor Page:<br \/>\n\u597d\u6d88\u606f\u662f Razor Pages \u4f1a\u81ea\u52a8\u4fdd\u62a4\u60a8\u514d\u53d7 CSRF \u653b\u51fb\u3002Form Tag Helper \u4f1a\u81ea\u52a8\u8bbe\u7f6e\u9632\u4f2a\u4ee4\u724c Cookie\uff0c\u5e76\u5c06\u8be5\u4ee4\u724c\u5448\u73b0\u5230\u5e94\u7528\u4e2d\u6bcf\u4e2a<code>&lt;form&gt;<\/code>\u5143\u7d20\u7684\u540d\u4e3a _RequestVerificationToken \u7684\u9690\u85cf\u5b57\u6bb5\uff08\u9664\u975e\u60a8\u4e13\u95e8\u7981\u7528\u5b83\u4eec\uff09\u3002\u4f8b\u5982\uff0c\u4ee5\u8fd9\u4e2a\u7b80\u5355\u7684 Razor \u6a21\u677f\u4e3a\u4f8b\uff0c\u8be5\u6a21\u677f\u56de\u53d1\u5230\u540c\u4e00 Razor \u9875\u9762\uff1a<\/p>\n<pre><code>&lt;form method=&quot;post&quot;&gt;\n&lt;label&gt;Amount&lt;\/label&gt;\n&lt;input type=&quot;number&quot; name=&quot;amount&quot; \/&gt;\n&lt;button type=&quot;submit&quot;&gt;Withdraw funds&lt;\/button&gt;\n&lt;\/form&gt;<\/code><\/pre>\n<p>When rendered to HTML, the antiforgery token is stored in the hidden \ufb01eld and is posted back with a legitimate request:<br \/>\n\u5f53\u5448\u73b0\u4e3a HTML \u65f6\uff0c\u9632\u4f2a\u4ee4\u724c\u5b58\u50a8\u5728 hidden \u5b57\u6bb5\u4e2d\uff0c\u5e76\u901a\u8fc7\u5408\u6cd5\u8bf7\u6c42\u53d1\u56de\uff1a<\/p>\n<pre><code>&lt;form method=&quot;post&quot;&gt;\n&lt;label&gt;Amount&lt;\/label&gt;\n&lt;input type=&quot;number&quot; name=&quot;amount&quot; \/&gt;\n&lt;button type=&quot;submit&quot; &gt;Withdraw funds&lt;\/button&gt;\n&lt;input name=&quot;__RequestVerificationToken&quot; type=&quot;hidden&quot;\n\nvalue=&quot;CfDJ8Daz26qb0hBGsw7QCK&quot;\/&gt;\n\n&lt;\/form&gt;<\/code><\/pre>\n<p>ASP.NET Core automatically adds the antiforgery tokens to every form, and Razor Pages automatically validates them. The framework ensures that the antiforgery tokens exist in both the cookie and the form data, ensures that they match, and rejects any requests where they don\u2019t.<br \/>\nASP.NET Core \u4f1a\u81ea\u52a8\u5c06\u9632\u4f2a\u4ee4\u724c\u6dfb\u52a0\u5230\u6bcf\u4e2a\u8868\u5355\uff0cRazor Pages \u4f1a\u81ea\u52a8\u9a8c\u8bc1\u5b83\u4eec\u3002\u8be5\u6846\u67b6\u786e\u4fdd\u9632\u4f2a\u4ee4\u724c\u540c\u65f6\u5b58\u5728\u4e8e Cookie \u548c\u8868\u5355\u6570\u636e\u4e2d\uff0c\u786e\u4fdd\u5b83\u4eec\u5339\u914d\uff0c\u5e76\u62d2\u7edd\u4efb\u4f55\u4e0d\u5339\u914d\u7684\u8bf7\u6c42\u3002<\/p>\n<p>If you\u2019re using Model-View-Controller (MVC) controllers with views instead of Razor Pages, ASP.NET Core still adds the antiforgery tokens to every form. Unfortunately, it doesn\u2019t validate them for you. Instead, you must decorate your controllers and actions with the [ValidateAntiForgeryToken] attribute. This ensures that the antiforgery tokens exist in both the cookie and the form data, checks that they match, and rejects any requests in which they don\u2019t.<br \/>\n\u5982\u679c\u5c06\u6a21\u578b-\u89c6\u56fe-\u63a7\u5236\u5668 \uff08MVC\uff09 \u63a7\u5236\u5668\u4e0e\u89c6\u56fe\u800c\u4e0d\u662f Razor Pages \u4e00\u8d77\u4f7f\u7528\uff0c\u5219 ASP.NET Core \u4ecd\u4f1a\u5c06\u9632\u4f2a\u4ee4\u724c\u6dfb\u52a0\u5230\u6bcf\u4e2a\u8868\u5355\u4e2d\u3002\u4e0d\u5e78\u7684\u662f\uff0c\u5b83\u4e0d\u4f1a\u4e3a\u60a8\u9a8c\u8bc1\u5b83\u4eec\u3002\u76f8\u53cd\uff0c\u60a8\u5fc5\u987b\u4f7f\u7528 [ValidateAntiForgeryToken] \u5c5e\u6027\u4fee\u9970\u63a7\u5236\u5668\u548c\u4f5c\u3002\u8fd9\u53ef\u786e\u4fdd\u9632\u4f2a\u4ee4\u724c\u540c\u65f6\u5b58\u5728\u4e8e Cookie \u548c\u8868\u5355\u6570\u636e\u4e2d\uff0c\u68c0\u67e5\u5b83\u4eec\u662f\u5426\u5339\u914d\uff0c\u5e76\u62d2\u7edd\u5b83\u4eec\u4e0d\u5339\u914d\u7684\u4efb\u4f55\u8bf7\u6c42\u3002<\/p>\n<p><b>WARNING<\/b> ASP.NET Core doesn\u2019t automatically validate antiforgery tokens if you\u2019re using MVC controllers with Views. You must make sure to mark all vulnerable methods with [ValidateAntiForgeryToken] attributes instead, as described in the \u201cPrevent Cross-Site Request Forgery (XSRF\/CSRF) attacks in ASP.NET Core\u201d documentation: <a href=\"http:\/\/mng.bz\/QPPv\">http:\/\/mng.bz\/QPPv<\/a>. Note that if you\u2019re not using cookies for authentication, you are not vulnerable to CSRF attacks: CSRF attacks arise from attackers exploiting the fact that browsers automatically attach cookies to requests. No cookies, no problem!<br \/>\n\u8b66\u544a:ASP.NET \u5982\u679c\u60a8\u5c06 MVC \u63a7\u5236\u5668\u4e0e\u89c6\u56fe\u4e00\u8d77\u4f7f\u7528\uff0cCore \u4e0d\u4f1a\u81ea\u52a8\u9a8c\u8bc1\u9632\u4f2a\u4ee4\u724c\u3002\u60a8\u5fc5\u987b\u786e\u4fdd\u4f7f\u7528 [ValidateAntiForgeryToken] \u5c5e\u6027\u6807\u8bb0\u6240\u6709\u6613\u53d7\u653b\u51fb\u7684\u65b9\u6cd5\uff0c\u5982\u201c\u9632\u6b62 ASP.NET Core \u4e2d\u7684\u8de8\u7ad9\u70b9\u8bf7\u6c42\u4f2a\u9020 \uff08XSRF\/CSRF\uff09 \u653b\u51fb\u201d\u6587\u6863\u4e2d\u6240\u8ff0\uff1a<a href=\"http:\/\/mng.bz\/QPPv\u3002\u8bf7\u6ce8\u610f\uff0c\u5982\u679c\u60a8\u4e0d\u4f7f\u7528\">http:\/\/mng.bz\/QPPv\u3002\u8bf7\u6ce8\u610f\uff0c\u5982\u679c\u60a8\u4e0d\u4f7f\u7528<\/a> cookie \u8fdb\u884c\u8eab\u4efd\u9a8c\u8bc1\uff0c\u5219\u4e0d\u6613\u53d7\u5230 CSRF \u653b\u51fb\uff1aCSRF \u653b\u51fb\u662f\u7531\u4e8e\u653b\u51fb\u8005\u5229\u7528\u6d4f\u89c8\u5668\u81ea\u52a8\u5c06 cookie \u9644\u52a0\u5230\u8bf7\u6c42\u8fd9\u4e00\u4e8b\u5b9e\u800c\u5f15\u8d77\u7684\u3002\u6ca1\u6709 cookie\uff0c\u6ca1\u95ee\u9898\uff01<\/p>\n<p>Generally, you need to use antiforgery tokens only for POST, DELETE, and other dangerous request types that are used for modifying state. GET requests shouldn\u2019t be used for this purpose, so the framework doesn\u2019t require valid antiforgery tokens to call them. Razor Pages validates antiforgery tokens for dangerous verbs like POST and ignores safe verbs like GET. As long as you create your app following this pattern\u200c\u200c (and you should!), the framework does the right thing to keep you safe.<br \/>\n\u901a\u5e38\uff0c\u60a8\u53ea\u9700\u5c06\u9632\u4f2a\u4ee4\u724c\u7528\u4e8e POST\u3001DELETE \u548c\u5176\u4ed6\u7528\u4e8e\u4fee\u6539\u72b6\u6001\u7684\u5371\u9669\u8bf7\u6c42\u7c7b\u578b\u3002GET \u8bf7\u6c42\u4e0d\u5e94\u7528\u4e8e\u6b64\u76ee\u7684\uff0c\u56e0\u6b64\u6846\u67b6\u4e0d\u9700\u8981\u6709\u6548\u7684\u9632\u4f2a\u4ee4\u724c\u6765\u8c03\u7528\u5b83\u4eec\u3002Razor Pages \u4f1a\u9a8c\u8bc1\u5371\u9669\u52a8\u8bcd\uff08\u5982 POST\uff09\u7684\u9632\u4f2a\u4ee4\u724c\uff0c\u5e76\u5ffd\u7565\u5b89\u5168\u52a8\u8bcd\uff08\u5982 GET\uff09\u3002\u53ea\u8981\u4f60\u6309\u7167\u8fd9\u79cd\u6a21\u5f0f\u521b\u5efa\u4f60\u7684\u5e94\u7528\u7a0b\u5e8f\uff08\u4f60\u5e94\u8be5\u8fd9\u6837\u505a\uff09\uff0c\u6846\u67b6\u5c31\u4f1a\u505a\u6b63\u786e\u7684\u4e8b\u60c5\u6765\u4fdd\u8bc1\u4f60\u7684\u5b89\u5168\u3002<\/p>\n<p>If you need to explicitly ignore antiforgery tokens on a Razor Page for some reason, you can disable the validation by applying the [IgnoreAntiforgeryToken] attribute to a Razor Page\u2019s PageModel. This bypasses the framework protections for those cases when you\u2019re doing something that you know is safe and doesn\u2019t need protecting, but in most cases it\u2019s better to play it safe and validate.\u200c<br \/>\n\u5982\u679c\u51fa\u4e8e\u67d0\u79cd\u539f\u56e0\u9700\u8981\u663e\u5f0f\u5ffd\u7565 Razor \u9875\u9762\u4e0a\u7684\u9632\u4f2a\u4ee4\u724c\uff0c\u53ef\u4ee5\u901a\u8fc7\u5c06 [IgnoreAntiforgeryToken] \u5c5e\u6027\u5e94\u7528\u4e8e Razor \u9875\u9762\u7684 PageModel \u6765\u7981\u7528\u9a8c\u8bc1\u3002\u5f53\u60a8\u6267\u884c\u4e00\u4e9b\u5df2\u77e5\u5b89\u5168\u4e14\u4e0d\u9700\u8981\u4fdd\u62a4\u7684\u4f5c\u65f6\uff0c\u8fd9\u5c06\u7ed5\u8fc7\u6846\u67b6\u4fdd\u62a4\uff0c\u4f46\u5728\u5927\u591a\u6570\u60c5\u51b5\u4e0b\uff0c\u6700\u597d\u8c28\u614e\u884c\u4e8b\u5e76\u8fdb\u884c\u9a8c\u8bc1\u3002<\/p>\n<p>CSRF attacks can be a tricky thing to get your head around from a technical point of view, but for the most part everything should work without much e\ufb00ort on your part.<br \/>\n\u4ece\u6280\u672f\u89d2\u5ea6\u6765\u770b\uff0cCSRF \u653b\u51fb\u53ef\u80fd\u662f\u4e00\u4ef6\u68d8\u624b\u7684\u4e8b\u60c5\uff0c\u4f46\u5728\u5927\u591a\u6570\u60c5\u51b5\u4e0b\uff0c\u4e00\u5207\u90fd\u5e94\u8be5\u53ef\u4ee5\u6b63\u5e38\u5de5\u4f5c\uff0c\u800c\u65e0\u9700\u60a8\u4ed8\u51fa\u592a\u591a\u52aa\u529b\u3002<\/p>\n<p>Razor adds antiforgery tokens to your forms, and the Razor Pages framework takes care of validation for you.<br \/>\nRazor \u5c06\u9632\u4f2a\u4ee4\u724c\u6dfb\u52a0\u5230\u60a8\u7684\u8868\u5355\u4e2d\uff0cRazor Pages \u6846\u67b6\u4f1a\u4e3a\u60a8\u5904\u7406\u9a8c\u8bc1\u3002<\/p>\n<p>Things get trickier if you\u2019re making a lot of requests to an API using JavaScript, and you\u2019re posting JavaScript Object Notation (JSON) objects rather than form data. In these cases, you won\u2019t be able to send the veri\ufb01cation token as part of a form (because you\u2019re sending JSON), so you\u2019ll need to add it as a header in the request instead. Microsoft\u2019s documentation \u201cPrevent Cross-Site Request Forgery (XSRF\/ CSRF) attacks in ASP.NET Core\u201d contains an example of adding the header in JavaScript and validating it in your application. See <a href=\"http:\/\/mng.bz\/XNNa\">http:\/\/mng.bz\/XNNa<\/a>.\u200c<br \/>\n\u5982\u679c\u60a8\u4f7f\u7528 JavaScript \u5411 API \u53d1\u51fa\u5927\u91cf\u8bf7\u6c42\uff0c\u5e76\u4e14\u60a8\u53d1\u5e03\u7684\u662f JavaScript \u5bf9\u8c61\u8868\u793a\u6cd5 \uff08JSON\uff09 \u5bf9\u8c61\u800c\u4e0d\u662f\u8868\u5355\u6570\u636e\uff0c\u90a3\u4e48\u4e8b\u60c5\u5c31\u4f1a\u53d8\u5f97\u66f4\u52a0\u68d8\u624b\u3002\u5728\u8fd9\u4e9b\u60c5\u51b5\u4e0b\uff0c\u60a8\u5c06\u65e0\u6cd5\u5c06\u9a8c\u8bc1\u4ee4\u724c\u4f5c\u4e3a\u8868\u5355\u7684\u4e00\u90e8\u5206\u53d1\u9001\uff08\u56e0\u4e3a\u60a8\u53d1\u9001\u7684\u662f JSON\uff09\uff0c\u56e0\u6b64\u60a8\u9700\u8981\u5c06\u5176\u4f5c\u4e3a\u6807\u5934\u6dfb\u52a0\u5230\u8bf7\u6c42\u4e2d\u3002Microsoft \u7684\u6587\u6863\u201c\u9632\u6b62 ASP.NET Core \u4e2d\u7684\u8de8\u7ad9\u70b9\u8bf7\u6c42\u4f2a\u9020 \uff08XSRF\/CSRF\uff09 \u653b\u51fb\u201d\u5305\u542b\u5728 JavaScript \u4e2d\u6dfb\u52a0\u6807\u5934\u5e76\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u9a8c\u8bc1\u5b83\u7684\u793a\u4f8b\u3002\u8bf7\u53c2\u9605 <a href=\"http:\/\/mng.bz\/XNNa\">http:\/\/mng.bz\/XNNa<\/a>\u3002<\/p>\n<p><b>TIP<\/b>  If you\u2019re not using cookie authentication and instead have a single-page application (SPA) that sends authentication tokens in a header, the good news is that you don\u2019t have to worry about CSRF at all! Malicious sites can send only cookies, not headers, to your API, so they can\u2019t make authenticated requests.<br \/>\n\u63d0\u793a:\u5982\u679c\u60a8\u4e0d\u4f7f\u7528 cookie \u8eab\u4efd\u9a8c\u8bc1\uff0c\u800c\u662f\u62e5\u6709\u5728 Headers \u4e2d\u53d1\u9001\u8eab\u4efd\u9a8c\u8bc1\u4ee4\u724c\u7684\u5355\u9875\u5e94\u7528\u7a0b\u5e8f \uff08SPA\uff09\uff0c\u90a3\u4e48\u597d\u6d88\u606f\u662f\uff0c\u60a8\u6839\u672c\u4e0d\u9700\u8981\u62c5\u5fc3 CSRF\uff01\u6076\u610f\u7f51\u7ad9\u53ea\u80fd\u5411\u60a8\u7684 API \u53d1\u9001 Cookie\uff0c\u800c\u4e0d\u80fd\u53d1\u9001\u6807\u5934\uff0c\u56e0\u6b64\u5b83\u4eec\u65e0\u6cd5\u53d1\u51fa\u7ecf\u8fc7\u8eab\u4efd\u9a8c\u8bc1\u7684\u8bf7\u6c42\u3002<\/p>\n<blockquote>\n<p>Generating unique tokens with the data protection APIs<br \/>\n\u4f7f\u7528\u6570\u636e\u4fdd\u62a4 API \u751f\u6210\u552f\u4e00\u4ee4\u724c<\/p>\n<p>The antiforgery tokens used to prevent CSRF attacks rely on the ability of the framework to use strong symmetric encryption to encrypt and decrypt data. Encryption algorithms typically rely on one or more keys, which are used to initialize the encryption and to make the process reproducible. If you have the key, you can encrypt and decrypt data; without it, the data is secure.<br \/>\n\u7528\u4e8e\u9632\u6b62 CSRF \u653b\u51fb\u7684\u9632\u4f2a\u4ee4\u724c\u4f9d\u8d56\u4e8e\u6846\u67b6\u4f7f\u7528\u5f3a\u5bf9\u79f0\u52a0\u5bc6\u6765\u52a0\u5bc6\u548c\u89e3\u5bc6\u6570\u636e\u7684\u80fd\u529b\u3002\u52a0\u5bc6\u7b97\u6cd5\u901a\u5e38\u4f9d\u8d56\u4e8e\u4e00\u4e2a\u6216\u591a\u4e2a\u5bc6\u94a5\uff0c\u8fd9\u4e9b\u5bc6\u94a5\u7528\u4e8e\u521d\u59cb\u5316\u52a0\u5bc6\u5e76\u4f7f\u8fc7\u7a0b\u53ef\u91cd\u73b0\u3002\u5982\u679c\u4f60\u6709\u5bc6\u94a5\uff0c\u4f60\u53ef\u4ee5\u52a0\u5bc6\u548c\u89e3\u5bc6\u6570\u636e;\u6ca1\u6709\u5b83\uff0c\u6570\u636e\u662f\u5b89\u5168\u7684\u3002<\/p>\n<p>In ASP.NET Core, encryption is handled by the data protection APIs. They\u2019re used to create the antiforgery tokens, encrypt authentication cookies, and generate secure tokens in general. Crucially, they also control the management of the key \ufb01les that are used for encryption. A key \ufb01le is a small XML \ufb01le that contains the random key value used for encryption in ASP.NET Core apps. It\u2019s critical that it\u2019s stored securely. If an attacker got hold of it, they could impersonate any user of your app and generally do bad things!<br \/>\n\u5728 ASP.NET Core \u4e2d\uff0c\u52a0\u5bc6\u7531\u6570\u636e\u4fdd\u62a4 API \u5904\u7406\u3002\u5b83\u4eec\u901a\u5e38\u7528\u4e8e\u521b\u5efa\u9632\u4f2a\u4ee4\u724c\u3001\u52a0\u5bc6\u8eab\u4efd\u9a8c\u8bc1 Cookie \u548c\u751f\u6210\u5b89\u5168\u4ee4\u724c\u3002\u81f3\u5173\u91cd\u8981\u7684\u662f\uff0c\u5b83\u4eec\u8fd8\u63a7\u5236\u7528\u4e8e\u52a0\u5bc6\u7684\u5bc6\u94a5\u6587\u4ef6\u7684\u7ba1\u7406\u3002\u5bc6\u94a5\u6587\u4ef6\u662f\u4e00\u4e2a\u5c0f\u578b XML \u6587\u4ef6\uff0c\u5176\u4e2d\u5305\u542b\u7528\u4e8e\u5728 ASP.NET Core \u5e94\u7528\u7a0b\u5e8f\u4e2d\u52a0\u5bc6\u7684\u968f\u673a\u5bc6\u94a5\u503c\u3002\u5b89\u5168\u5b58\u50a8\u81f3\u5173\u91cd\u8981\u3002\u5982\u679c\u653b\u51fb\u8005\u638c\u63e1\u4e86\u5b83\uff0c\u4ed6\u4eec\u5c31\u53ef\u4ee5\u5192\u5145\u60a8\u5e94\u7528\u7a0b\u5e8f\u7684\u4efb\u4f55\u7528\u6237\uff0c\u5e76\u4e14\u901a\u5e38\u4f1a\u505a\u574f\u4e8b\uff01<\/p>\n<p>The data protection system stores the keys in a safe location, depending on how and where you host your app:<br \/>\n\u6570\u636e\u4fdd\u62a4\u7cfb\u7edf\u4f1a\u5c06\u5bc6\u94a5\u5b58\u50a8\u5728\u5b89\u5168\u7684\u4f4d\u7f6e\uff0c\u5177\u4f53\u53d6\u51b3\u4e8e\u60a8\u6258\u7ba1\u5e94\u7528\u7684\u65b9\u5f0f\u548c\u4f4d\u7f6e\uff1a<\/p>\n<\/blockquote>\n<p>\u2022  Azure Web App\u2014In a special synced folder, shared between regions<br \/>\nAzure Web \u5e94\u7528\u7a0b\u5e8f - \u4f4d\u4e8e\u7279\u6b8a\u540c\u6b65\u6587\u4ef6\u5939\u4e2d\uff0c\u5728\u533a\u57df\u4e4b\u95f4\u5171\u4eab<\/p>\n<p>\u2022  IIS without user pro\ufb01le\u2014Encrypted in the registry<br \/>\n\u6ca1\u6709\u7528\u6237\u914d\u7f6e\u6587\u4ef6\u7684 IIS - \u5728\u6ce8\u518c\u8868\u4e2d\u52a0\u5bc6<\/p>\n<p>\u2022  Account with user pro\ufb01le\u2014In %LOCALAPPDATA%\\ASP.NET\\DataProtection-Keys on Windows, or ~\/.aspnet\/DataProtection-Keys on Linux or macOS<br \/>\n\u5177\u6709\u7528\u6237\u914d\u7f6e\u6587\u4ef6\u7684\u5e10\u6237 - \u5728 Windows \u4e0a\u4f4d\u4e8e %LOCALAPPDATA%\\ASP.NET\\DataProtection-Keys \u4e2d\uff0c\u5728 Linux \u6216 macOS \u4e0a\u4f4d\u4e8e ~\/.aspnet\/DataProtection-Keys \u4e2d<\/p>\n<p>\u2022  All other cases\u2014In memory; when the app restarts, the keys will be lost<br \/>\n\u6240\u6709\u5176\u4ed6\u60c5\u51b5 - \u5728\u5185\u5b58\u4e2d;\u5f53\u5e94\u7528\u7a0b\u5e8f\u91cd\u65b0\u542f\u52a8\u65f6\uff0c\u5bc6\u94a5\u5c06\u4e22\u5931<\/p>\n<blockquote>\n<p>So why do you care? For your app to be able to read your users\u2019 authentication cookies, it must decrypt them by using the same key that was used to encrypt them. If you\u2019re running in a web-farm scenario, by default each server has its own key and won\u2019t be able to read cookies encrypted by other servers.<br \/>\n\u90a3\u4e48\uff0c\u60a8\u4e3a\u4ec0\u4e48\u5173\u5fc3\u5462\uff1f\u4e3a\u4e86\u4f7f\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u80fd\u591f\u8bfb\u53d6\u7528\u6237\u7684\u8eab\u4efd\u9a8c\u8bc1 Cookie\uff0c\u5b83\u5fc5\u987b\u4f7f\u7528\u7528\u4e8e\u52a0\u5bc6\u7528\u6237\u7684\u76f8\u540c\u5bc6\u94a5\u5bf9\u5176\u8fdb\u884c\u89e3\u5bc6\u3002\u5982\u679c\u60a8\u5728 Web \u573a\u65b9\u6848\u4e2d\u8fd0\u884c\uff0c\u5219\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u6bcf\u4e2a\u670d\u52a1\u5668\u90fd\u6709\u81ea\u5df1\u7684\u5bc6\u94a5\uff0c\u5e76\u4e14\u65e0\u6cd5\u8bfb\u53d6\u7531\u5176\u4ed6\u670d\u52a1\u5668\u52a0\u5bc6\u7684 Cookie\u3002<\/p>\n<p>To get around this, you must con\ufb01gure your app to store its data protection keys in a central location. This could be a shared folder on a hard drive, a Redis instance, or an Azure blob storage instance, for example.<br \/>\n\u8981\u89e3\u51b3\u6b64\u95ee\u9898\uff0c\u60a8\u5fc5\u987b\u5c06\u5e94\u7528\u7a0b\u5e8f\u914d\u7f6e\u4e3a\u5c06\u5176\u6570\u636e\u4fdd\u62a4\u5bc6\u94a5\u5b58\u50a8\u5728\u4e00\u4e2a\u4e2d\u5fc3\u4f4d\u7f6e\u3002\u4f8b\u5982\uff0c\u8fd9\u53ef\u4ee5\u662f\u786c\u76d8\u9a71\u52a8\u5668\u4e0a\u7684\u5171\u4eab\u6587\u4ef6\u5939\u3001Redis \u5b9e\u4f8b\u6216 Azure Blob \u5b58\u50a8\u5b9e\u4f8b\u3002<\/p>\n<p>Microsoft\u2019s documentation on the data protection APIs is extremely detailed, but it can be overwhelming. I recommend reading the section on con\ufb01guring data protection, (\u201cCon\ufb01gure ASP.NET Core Data Protection,\u201d <a href=\"http:\/\/mng.bz\/d40i\">http:\/\/mng.bz\/d40i<\/a>) and con\ufb01guring a key storage provider for use in a web- farm scenario (\u201cKey storage providers in ASP.NET Core,\u201d <a href=\"http:\/\/mng.bz\/5pW6\">http:\/\/mng.bz\/5pW6<\/a>). I also have an introduction to the data protection APIs on my blog at <a href=\"http:\/\/mng.bz\/yQQd\">http:\/\/mng.bz\/yQQd<\/a>.<br \/>\nMicrosoft \u5173\u4e8e\u6570\u636e\u4fdd\u62a4 API \u7684\u6587\u6863\u975e\u5e38\u8be6\u7ec6\uff0c\u4f46\u53ef\u80fd\u4f1a\u8ba9\u4eba\u4e0d\u77e5\u6240\u63aa\u3002\u6211\u5efa\u8bae\u9605\u8bfb\u6709\u5173\u914d\u7f6e\u6570\u636e\u4fdd\u62a4\u7684\u90e8\u5206\uff08\u201c\u914d\u7f6e ASP.NET Core \u6570\u636e\u4fdd\u62a4\u201d\uff0c<a href=\"http:\/\/mng.bz\/d40i\">http:\/\/mng.bz\/d40i<\/a> \u5e74\uff09\u548c\u914d\u7f6e\u7528\u4e8e Web \u573a\u65b9\u6848\u7684\u5bc6\u94a5\u5b58\u50a8\u63d0\u4f9b\u7a0b\u5e8f\uff08\u201cASP.NET Core \u4e2d\u7684\u5bc6\u94a5\u5b58\u50a8\u63d0\u4f9b\u7a0b\u5e8f\u201d\uff0c<a href=\"http:\/\/mng.bz\/5pW6\">http:\/\/mng.bz\/5pW6<\/a> \u5e74\uff09\u3002\u6211\u8fd8\u5728\u6211\u7684\u535a\u5ba2 <a href=\"http:\/\/mng.bz\/yQQd\">http:\/\/mng.bz\/yQQd<\/a> \u4e0a\u4ecb\u7ecd\u4e86\u6570\u636e\u4fdd\u62a4 API\u3002<\/p>\n<\/blockquote>\n<p>It\u2019s worth clarifying that the CSRF vulnerability discussed in this section requires that a malicious site does a full form POST to your app. The malicious site can\u2019t make the request to your API using client-side-only JavaScript, as browsers block JavaScript requests to your API that are from a di\ufb00erent origin.<br \/>\n\u503c\u5f97\u6f84\u6e05\u7684\u662f\uff0c\u672c\u8282\u4e2d\u8ba8\u8bba\u7684 CSRF \u6f0f\u6d1e\u8981\u6c42\u6076\u610f\u7f51\u7ad9\u5bf9\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u6267\u884c\u5b8c\u6574\u5f62\u5f0f\u7684 POST\u3002\u6076\u610f\u7ad9\u70b9\u65e0\u6cd5\u4f7f\u7528\u4ec5\u9650\u5ba2\u6237\u7aef\u7684 JavaScript \u5411\u60a8\u7684 API \u53d1\u51fa\u8bf7\u6c42\uff0c\u56e0\u4e3a\u6d4f\u89c8\u5668\u4f1a\u963b\u6b62\u6765\u81ea\u4e0d\u540c\u6765\u6e90\u7684 JavaScript \u8bf7\u6c42\u3002<\/p>\n<p>This is a safety feature, but it can often cause you problems. If you\u2019re building a client-side SPA, or even if you have a little JavaScript on an otherwise server-side rendered app, you may need to make such cross-origin requests. In the next section I describe a common scenario you\u2019re likely to run into and show how you can modify your apps to work around Pit.<br \/>\n\u8fd9\u662f\u4e00\u9879\u5b89\u5168\u529f\u80fd\uff0c\u4f46\u5b83\u901a\u5e38\u4f1a\u7ed9\u60a8\u5e26\u6765\u9ebb\u70e6\u3002\u5982\u679c\u60a8\u6b63\u5728\u6784\u5efa\u5ba2\u6237\u7aef SPA\uff0c\u6216\u8005\u5373\u4f7f\u60a8\u5728\u5176\u4ed6\u670d\u52a1\u5668\u7aef\u5448\u73b0\u7684\u5e94\u7528\u7a0b\u5e8f\u4e0a\u6709\u4e00\u70b9 JavaScript\uff0c\u4e5f\u53ef\u80fd\u9700\u8981\u53d1\u51fa\u6b64\u7c7b\u8de8\u57df\u8bf7\u6c42\u3002\u5728\u4e0b\u4e00\u8282\u4e2d\uff0c\u6211\u5c06\u4ecb\u7ecd\u60a8\u53ef\u80fd\u4f1a\u9047\u5230\u7684\u5e38\u89c1\u573a\u666f\uff0c\u5e76\u5c55\u793a\u5982\u4f55\u4fee\u6539\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u4ee5\u89e3\u51b3 Pit \u95ee\u9898\u3002<\/p>\n<h2>29.3 Calling your web APIs from other domains using CORS\u200c<\/h2>\n<p>29.3 \u4f7f\u7528 CORS \u4ece\u5176\u4ed6\u57df\u8c03\u7528 Web API<\/p>\n<p>In this section you\u2019ll learn about cross-origin resource sharing (CORS), a protocol to allow JavaScript to make requests from one domain to another. CORS is a frequent area of confusion for many developers, so this section describes why it\u2019s necessary and how CORS headers work. You\u2019ll then learn how to add CORS to both your whole application and speci\ufb01c web API actions, and how to con\ufb01gure multiple CORS policies for your application.<br \/>\n\u5728\u672c\u8282\u4e2d\uff0c\u60a8\u5c06\u4e86\u89e3\u8de8\u57df\u8d44\u6e90\u5171\u4eab \uff08CORS\uff09\uff0c\u8fd9\u662f\u4e00\u79cd\u5141\u8bb8 JavaScript \u4ece\u4e00\u4e2a\u57df\u5411\u53e6\u4e00\u4e2a\u57df\u53d1\u51fa\u8bf7\u6c42\u7684\u534f\u8bae\u3002CORS \u662f\u8bb8\u591a\u5f00\u53d1\u4eba\u5458\u7ecf\u5e38\u6df7\u6dc6\u7684\u9886\u57df\uff0c\u56e0\u6b64\u672c\u8282\u4ecb\u7ecd\u4e3a\u4ec0\u4e48\u9700\u8981 CORS \u4ee5\u53ca CORS \u6807\u5934\u7684\u5de5\u4f5c\u539f\u7406\u3002\u7136\u540e\uff0c\u60a8\u5c06\u4e86\u89e3\u5982\u4f55\u5c06 CORS \u6dfb\u52a0\u5230\u6574\u4e2a\u5e94\u7528\u7a0b\u5e8f\u548c\u7279\u5b9a Web API\u4f5c\uff0c\u4ee5\u53ca\u5982\u4f55\u4e3a\u5e94\u7528\u7a0b\u5e8f\u914d\u7f6e\u591a\u4e2a CORS \u7b56\u7565\u3002<\/p>\n<p>As you\u2019ve already seen, CSRF attacks can be powerful, but they would be even more dangerous if it weren\u2019t for browsers implementing the same-origin policy. This policy blocks apps from using JavaScript to call a web API at a di\ufb00erent location unless the web API explicitly allows it.<br \/>\n\u6b63\u5982\u4f60\u5df2\u7ecf\u770b\u5230\u7684\uff0cCSRF \u653b\u51fb\u53ef\u80fd\u5f88\u5f3a\u5927\uff0c\u4f46\u5982\u679c\u4e0d\u662f\u6d4f\u89c8\u5668\u5b9e\u65bd\u540c\u6e90\u7b56\u7565\uff0c\u5b83\u4eec\u4f1a\u66f4\u52a0\u5371\u9669\u3002\u6b64\u653f\u7b56\u7981\u6b62\u5e94\u7528\u4f7f\u7528 JavaScript \u8c03\u7528\u4f4d\u4e8e\u5176\u4ed6\u4f4d\u7f6e\u7684 Web API\uff0c\u9664\u975e Web API \u660e\u786e\u5141\u8bb8\u3002<\/p>\n<p><b>DEFINITION<\/b> Origins are deemed to be the same if they match the scheme (HTTP or HTTPS), domain (example.com), and port (80 by default for HTTP and 443 for HTTPS). If an app attempts to access a resource using JavaScript, and the origins aren\u2019t identical, the browser blocks the request.<br \/>\n\u5b9a\u4e49:\u5982\u679c\u6e90\u4e0e\u65b9\u6848\uff08HTTP \u6216 HTTPS\uff09\u3001\u57df \uff08example.com\uff09 \u548c\u7aef\u53e3\uff08HTTP \u9ed8\u8ba4\u4e3a 80\uff0cHTTPS \u4e3a 443\uff09\u5339\u914d\uff0c\u5219\u8ba4\u4e3a\u6e90\u76f8\u540c\u3002\u5982\u679c\u5e94\u7528\u7a0b\u5e8f\u5c1d\u8bd5\u4f7f\u7528 JavaScript \u8bbf\u95ee\u8d44\u6e90\uff0c\u5e76\u4e14\u6765\u6e90\u4e0d\u76f8\u540c\uff0c\u5219\u6d4f\u89c8\u5668\u4f1a\u963b\u6b62\u8be5\u8bf7\u6c42\u3002<\/p>\n<p>The same-origin policy is strict. The origins of the two URLs must be identical for the request to be allowed. For example, the following origins are the same:<br \/>\n\u540c\u6e90\u7b56\u7565\u5f88\u4e25\u683c\u3002\u4e24\u4e2a URL \u7684\u6765\u6e90\u5fc5\u987b\u76f8\u540c\uff0c\u624d\u80fd\u5141\u8bb8\u8bf7\u6c42\u3002\u4f8b\u5982\uff0c\u4ee5\u4e0b\u6765\u6e90\u662f\u76f8\u540c\u7684\uff1a<\/p>\n<p>\u2022  <a href=\"http:\/\/example.com\/home\">http:\/\/example.com\/home<\/a><br \/>\n\u2022  <a href=\"http:\/\/example.com\/site.css\">http:\/\/example.com\/site.css<\/a><\/p>\n<p>The paths are di\ufb00erent for these two URLs (\/home and \/site.css), but the scheme, domain, and port (80) are identical. So if you were on the home page of your app, you could request the \/site.css \ufb01le using JavaScript without any problems.<br \/>\n\u8fd9\u4e24\u4e2a URL \uff08\/home \u548c \/site.css\uff09 \u7684\u8def\u5f84\u4e0d\u540c\uff0c\u4f46 scheme\u3001domain \u548c port \uff0880\uff09 \u76f8\u540c\u3002\u56e0\u6b64\uff0c\u5982\u679c\u4f60\u5728\u5e94\u7528\u7a0b\u5e8f\u7684\u4e3b\u9875\u4e0a\uff0c\u4f60\u53ef\u4ee5\u4f7f\u7528 JavaScript \u8bf7\u6c42 \/site.css \u6587\u4ef6\uff0c\u800c\u4e0d\u4f1a\u51fa\u73b0\u4efb\u4f55\u95ee\u9898\u3002<\/p>\n<p>By contrast, the origins of the following sites are di\ufb00erent, so you couldn\u2019t request any of these URLs using JavaScript from the <a href=\"http:\/\/example.com\">http:\/\/example.com<\/a> origin:<br \/>\n\u76f8\u6bd4\u4e4b\u4e0b\uff0c\u4ee5\u4e0b\u7f51\u7ad9\u7684\u6765\u6e90\u4e0d\u540c\uff0c\u56e0\u6b64\u60a8\u65e0\u6cd5\u4f7f\u7528 JavaScript \u4ece <a href=\"http:\/\/example.com\">http:\/\/example.com<\/a> \u6765\u6e90\u8bf7\u6c42\u8fd9\u4e9b URL \u4e2d\u7684\u4efb\u4f55\u4e00\u4e2a\uff1a<\/p>\n<p>\u2022  <a href=\"https:\/\/example.com\u2014Di\ufb00erent\">https:\/\/example.com\u2014Di\ufb00erent<\/a> scheme (https)<\/p>\n<p>\u2022  <a href=\"http:\/\/www.example.com\u2014Di\ufb00erent\">http:\/\/www.example.com\u2014Di\ufb00erent<\/a> domain (includes a subdomain)<\/p>\n<p>\u2022  <a href=\"http:\/\/example.com:5000\u2014Di\ufb00erent\">http:\/\/example.com:5000\u2014Di\ufb00erent<\/a> port (default HTTP port is 80)<\/p>\n<p>For simple apps, where you have a single web app handling all your functionality, this limitation might not be a problem, but it\u2019s extremely common for an app to make requests to another domain. For example, you might have an e- commerce site hosted at <a href=\"http:\/\/shopping.com\">http:\/\/shopping.com<\/a>, and you\u2019re attempting to load data from <a href=\"http:\/\/api.shop\">http:\/\/api.shop<\/a> ping.com to display details about the products available for sale. With this con\ufb01guration, you\u2019ll fall foul of the same-origin policy.Any attempt to make a request using JavaScript to the API domain will fail, with an error similar to \ufb01gure 29.6.<br \/>\n\u5bf9\u4e8e\u7b80\u5355\u7684\u5e94\u7528\u7a0b\u5e8f\uff0c\u60a8\u6709\u4e00\u4e2a Web \u5e94\u7528\u7a0b\u5e8f\u5904\u7406\u60a8\u7684\u6240\u6709\u529f\u80fd\uff0c\u6b64\u9650\u5236\u53ef\u80fd\u4e0d\u662f\u95ee\u9898\uff0c\u4f46\u5e94\u7528\u7a0b\u5e8f\u5411\u53e6\u4e00\u4e2a\u57df\u53d1\u51fa\u8bf7\u6c42\u7684\u60c5\u51b5\u975e\u5e38\u5e38\u89c1\u3002\u4f8b\u5982\uff0c\u60a8\u53ef\u80fd\u5728 <a href=\"http:\/\/shopping.com\">http:\/\/shopping.com<\/a> \u4e0a\u6258\u7ba1\u4e86\u4e00\u4e2a\u7535\u5b50\u5546\u52a1\u7f51\u7ad9\uff0c\u5e76\u4e14\u60a8\u6b63\u5728\u5c1d\u8bd5\u4ece <a href=\"http:\/\/api.shop\">http:\/\/api.shop<\/a> ping.com \u52a0\u8f7d\u6570\u636e\u4ee5\u663e\u793a\u6709\u5173\u53ef\u4f9b\u9500\u552e\u4ea7\u54c1\u7684\u8be6\u7ec6\u4fe1\u606f\u3002\u4f7f\u7528\u6b64\u914d\u7f6e\uff0c\u60a8\u5c06\u8fdd\u53cd\u540c\u6e90\u7b56\u7565\u3002\u4efb\u4f55\u4f7f\u7528 JavaScript \u5411 API \u57df\u53d1\u51fa\u8bf7\u6c42\u7684\u5c1d\u8bd5\u90fd\u5c06\u5931\u8d25\uff0c\u5e76\u51fa\u73b0\u7c7b\u4f3c\u4e8e\u56fe 29.6 \u7684\u9519\u8bef\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/2906.jpg\" alt=\"alt text\" \/><\/p>\n<p>Figure 29.6 The console log for a failed cross-origin request. Chrome has blocked a cross-origin request from the app <a href=\"http:\/\/shopping.com:6333\">http:\/\/shopping.com:6333<\/a> to the API at <a href=\"http:\/\/api.shopping.com:5111\">http:\/\/api.shopping.com:5111<\/a>.<br \/>\n\u56fe 29.6 \u5931\u8d25\u7684\u8de8\u57df\u8bf7\u6c42\u7684\u63a7\u5236\u53f0\u65e5\u5fd7\u3002Chrome \u5728 <a href=\"http:\/\/api.shopping.com:5111\">http:\/\/api.shopping.com:5111<\/a> \u65f6\u963b\u6b62\u4e86\u5e94\u7528 <a href=\"http:\/\/shopping.com:6333\">http:\/\/shopping.com:6333<\/a> \u5411 API \u53d1\u51fa\u7684\u8de8\u57df\u8bf7\u6c42\u3002<\/p>\n<p>The need to make cross-origin requests from JavaScript is increasingly common with the rise of client-side SPAs and the move away from monolithic apps. Luckily, there\u2019s a web standard that lets you work around this in a safe way; this standard is CORS. You can use CORS to control which apps can call your API, so you can enable scenarios like this one.<br \/>\n\u968f\u7740\u5ba2\u6237\u7aef SPA \u7684\u5174\u8d77\u548c\u4ece\u6574\u4f53\u5f0f\u5e94\u7528\u7a0b\u5e8f\u7684\u8f6c\u53d8\uff0c\u4ece JavaScript \u53d1\u51fa\u8de8\u57df\u8bf7\u6c42\u7684\u9700\u6c42\u8d8a\u6765\u8d8a\u666e\u904d\u3002\u5e78\u8fd0\u7684\u662f\uff0c\u6709\u4e00\u4e2a Web \u6807\u51c6\u53ef\u4ee5\u8ba9\u60a8\u4ee5\u5b89\u5168\u7684\u65b9\u5f0f\u89e3\u51b3\u8fd9\u4e2a\u95ee\u9898;\u8fd9\u4e2a\u6807\u51c6\u662f CORS\u3002\u60a8\u53ef\u4ee5\u4f7f\u7528 CORS \u6765\u63a7\u5236\u54ea\u4e9b\u5e94\u7528\u7a0b\u5e8f\u53ef\u4ee5\u8c03\u7528\u60a8\u7684 API\uff0c\u56e0\u6b64\u60a8\u53ef\u4ee5\u542f\u7528\u6b64\u7c7b\u65b9\u6848\u3002<\/p>\n<h3>29.3.1 Understanding CORS and how it works\u200c<\/h3>\n<p>29.3.1 \u4e86\u89e3 CORS \u53ca\u5176\u5de5\u4f5c\u539f\u7406<\/p>\n<p>CORS is a web standard that allows your web API to make statements about who can make cross-origin requests to it. For example, you could make statements such as these:<br \/>\nCORS \u662f\u4e00\u79cd Web \u6807\u51c6\uff0c\u5b83\u5141\u8bb8\u60a8\u7684 Web API \u58f0\u660e\u8c01\u53ef\u4ee5\u5411\u5176\u53d1\u51fa\u8de8\u57df\u8bf7\u6c42\u3002\u4f8b\u5982\uff0c\u60a8\u53ef\u4ee5\u505a\u51fa\u5982\u4e0b\u9648\u8ff0\uff1a<\/p>\n<p>\u2022  Allow cross-origin requests from <a href=\"https:\/\/shopping.com\">https:\/\/shopping.com<\/a> and <a href=\"https:\/\/app.shopping.com\">https:\/\/app.shopping.com<\/a>.<br \/>\n\u5141\u8bb8\u6765\u81ea <a href=\"https:\/\/shopping.com\">https:\/\/shopping.com<\/a> \u548c <a href=\"https:\/\/app.shopping.com\">https:\/\/app.shopping.com<\/a> \u7684\u8de8\u57df\u8bf7\u6c42\u3002<\/p>\n<p>\u2022  Allow only GET cross-origin requests.<br \/>\n\u4ec5\u5141\u8bb8 GET \u8de8\u57df\u8bf7\u6c42\u3002<\/p>\n<p>\u2022  Allow returning the Server header in responses to cross-origin requests.<br \/>\n\u5141\u8bb8\u5728\u54cd\u5e94\u8de8\u57df\u8bf7\u6c42\u65f6\u8fd4\u56de Server \u6807\u5934\u3002<\/p>\n<p>\u2022  Allow credentials (such as authentication cookies or authorization headers) to be sent with cross- origin requests.<br \/>\n\u5141\u8bb8\u901a\u8fc7\u8de8\u57df\u8bf7\u6c42\u53d1\u9001\u51ed\u636e \uff08\u4f8b\u5982\u8eab\u4efd\u9a8c\u8bc1 Cookie \u6216\u6388\u6743\u6807\u5934\uff09\u3002<\/p>\n<p>You can combine these rules into a policy and apply di\ufb00erent policies to di\ufb00erent endpoints of your API. You could apply a policy to your entire application or a di\ufb00erent policy to every API action.<br \/>\n\u60a8\u53ef\u4ee5\u5c06\u8fd9\u4e9b\u89c4\u5219\u5408\u5e76\u5230\u4e00\u4e2a\u7b56\u7565\u4e2d\uff0c\u5e76\u5c06\u4e0d\u540c\u7684\u7b56\u7565\u5e94\u7528\u4e8e API \u7684\u4e0d\u540c\u7ec8\u7aef\u8282\u70b9\u3002\u60a8\u53ef\u4ee5\u5c06\u7b56\u7565\u5e94\u7528\u4e8e\u6574\u4e2a\u5e94\u7528\u7a0b\u5e8f\uff0c\u4e5f\u53ef\u4ee5\u5c06\u4e0d\u540c\u7684\u7b56\u7565\u5e94\u7528\u4e8e\u6bcf\u4e2a API\u4f5c\u3002<\/p>\n<p>CORS works using HTTP headers. When your web API application receives a request, it sets special headers on the response to indicate whether cross-origin requests are allowed, which origins they\u2019re allowed from, and which HTTP verbs and headers the request can use\u2014pretty much everything about the request.<br \/>\nCORS \u4f7f\u7528 HTTP \u6807\u5934\u5de5\u4f5c\u3002\u5f53\u60a8\u7684 Web API \u5e94\u7528\u7a0b\u5e8f\u6536\u5230\u8bf7\u6c42\u65f6\uff0c\u5b83\u4f1a\u5728\u54cd\u5e94\u4e0a\u8bbe\u7f6e\u7279\u6b8a\u6807\u5934\uff0c\u4ee5\u6307\u793a\u662f\u5426\u5141\u8bb8\u8de8\u57df\u8bf7\u6c42\u3001\u5141\u8bb8\u5b83\u4eec\u6765\u81ea\u54ea\u4e9b\u6765\u6e90\u4ee5\u53ca\u8bf7\u6c42\u53ef\u4ee5\u4f7f\u7528\u54ea\u4e9b HTTP \u52a8\u8bcd\u548c\u6807\u5934 \u2014 \u51e0\u4e4e\u6db5\u76d6\u4e86\u6709\u5173\u8bf7\u6c42\u7684\u6240\u6709\u5185\u5bb9\u3002<\/p>\n<p>In some cases, before sending a real request to your API, the browser sends a pre\ufb02ight request, a request sent using the OPTIONS verb, which the browser uses to check whether it\u2019s allowed to make the real request. If the API sends back the correct headers, the browser sends the true cross-origin request, as shown in \ufb01gure 29.7.\u200c<br \/>\n\u5728\u67d0\u4e9b\u60c5\u51b5\u4e0b\uff0c\u5728\u5411 API \u53d1\u9001\u5b9e\u9645\u8bf7\u6c42\u4e4b\u524d\uff0c\u6d4f\u89c8\u5668\u4f1a\u53d1\u9001\u9884\u68c0\u8bf7\u6c42\uff0c\u5373\u4f7f\u7528 OPTIONS \u8c13\u8bcd\u53d1\u9001\u7684\u8bf7\u6c42\uff0c\u6d4f\u89c8\u5668\u4f7f\u7528\u8be5\u8bf7\u6c42\u6765\u68c0\u67e5\u662f\u5426\u5141\u8bb8\u53d1\u51fa\u5b9e\u9645\u8bf7\u6c42\u3002\u5982\u679c API \u53d1\u56de\u6b63\u786e\u7684 Headers\uff0c\u5219\u6d4f\u89c8\u5668\u4f1a\u53d1\u9001\u771f\u6b63\u7684\u8de8\u57df\u8bf7\u6c42\uff0c\u5982\u56fe 29.7 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/2907.jpg\" alt=\"alt text\" \/><\/p>\n<p>Figure 29.7 Two cross-origin requests. The response to the GET request doesn\u2019t contain any CORS headers, so the browser blocks the app from reading it, even though the response may contain data from the server. The second request requires a pre\ufb02ight OPTIONS request to check whether CORS is enabled. As the response contains CORS headers, the browser makes the real request and provides the response to the JavaScript app.<br \/>\n\u56fe 29.7 \u4e24\u4e2a\u8de8\u57df\u8bf7\u6c42\u3002\u5bf9 GET \u8bf7\u6c42\u7684\u54cd\u5e94\u4e0d\u5305\u542b\u4efb\u4f55 CORS \u6807\u5934\uff0c\u56e0\u6b64\u6d4f\u89c8\u5668\u4f1a\u963b\u6b62\u5e94\u7528\u7a0b\u5e8f\u8bfb\u53d6\u5b83\uff0c\u5373\u4f7f\u54cd\u5e94\u53ef\u80fd\u5305\u542b\u6765\u81ea\u670d\u52a1\u5668\u7684\u6570\u636e\u3002\u7b2c\u4e8c\u4e2a\u8bf7\u6c42\u9700\u8981\u9884\u68c0 OPTIONS \u8bf7\u6c42\u6765\u68c0\u67e5\u662f\u5426\u542f\u7528\u4e86 CORS\u3002\u7531\u4e8e\u54cd\u5e94\u5305\u542b CORS \u6807\u5934\uff0c\u56e0\u6b64\u6d4f\u89c8\u5668\u4f1a\u53d1\u51fa\u771f\u6b63\u7684\u8bf7\u6c42\u5e76\u5411 JavaScript \u5e94\u7528\u7a0b\u5e8f\u63d0\u4f9b\u54cd\u5e94\u3002<\/p>\n<p><b>TIP<\/b>  For a more detailed discussion of CORS, see CORS in Action, by Monsur Hossain (Manning, 2014), <a href=\"http:\/\/mng.bz\/aD41\">http:\/\/mng.bz\/aD41<\/a>.\u200c<br \/>\n\u63d0\u793a:\u6709\u5173 CORS \u7684\u66f4\u8be6\u7ec6\u8ba8\u8bba\uff0c\u8bf7\u53c2\u9605 CORS in Action\uff0cMonsur Hossain \u8457\uff08Manning\uff0c2014 \u5e74\uff09\uff0c<a href=\"http:\/\/mng.bz\/aD41\">http:\/\/mng.bz\/aD41<\/a>\u3002<\/p>\n<p>The CORS speci\ufb01cation, which you can \ufb01nd at <a href=\"http:\/\/mng.bz\/MBBB\">http:\/\/mng.bz\/MBBB<\/a>, is complicated, with a variety of headers, processes, and terminology to contend with. Fortunately, ASP.NET Core handles the details of the speci\ufb01cation for you, so your main concern is working out exactly who needs to access your API, and under what circumstances.<br \/>\nCORS \u89c4\u8303\uff08\u60a8\u53ef\u4ee5\u5728 <a href=\"http:\/\/mng.bz\/MBBB\">http:\/\/mng.bz\/MBBB<\/a> \u4e0a\u627e\u5230\uff09\u5f88\u590d\u6742\uff0c\u9700\u8981\u5904\u7406\u5404\u79cd\u6807\u5934\u3001\u6d41\u7a0b\u548c\u672f\u8bed\u3002\u5e78\u8fd0\u7684\u662f\uff0cASP.NET Core \u4f1a\u4e3a\u60a8\u5904\u7406\u89c4\u8303\u7684\u7ec6\u8282\uff0c\u56e0\u6b64\u60a8\u4e3b\u8981\u5173\u5fc3\u7684\u662f\u51c6\u786e\u786e\u5b9a\u8c01\u9700\u8981\u8bbf\u95ee\u60a8\u7684 API\uff0c\u4ee5\u53ca\u5728\u4ec0\u4e48\u60c5\u51b5\u4e0b\u9700\u8981\u8bbf\u95ee\u60a8\u7684 API\u3002<\/p>\n<h3>29.3.2 Adding a global CORS policy to your whole app\u200c<\/h3>\n<p>29.3.2 \u5411\u6574\u4e2a\u5e94\u7528\u7a0b\u5e8f\u6dfb\u52a0\u5168\u5c40 CORS \u7b56\u7565<\/p>\n<p>Typically, you shouldn\u2019t set up CORS for your APIs until you need it. Browsers block cross-origin communication for a reason: it closes an avenue of attack. They\u2019re not being awkward. Wait until you have an API hosted on a di\ufb00erent domain to the app that needs to access it.<br \/>\n\u901a\u5e38\uff0c\u9664\u975e\u9700\u8981\uff0c\u5426\u5219\u4e0d\u5e94\u4e3a API \u8bbe\u7f6e CORS\u3002\u6d4f\u89c8\u5668\u963b\u6b62\u8de8\u57df\u901a\u4fe1\u662f\u6709\u539f\u56e0\u7684\uff1a\u5b83\u5173\u95ed\u4e86\u653b\u51fb\u9014\u5f84\u3002\u4ed6\u4eec\u6ca1\u6709\u5c34\u5c2c\u3002\u7b49\u5f85\uff0c\u76f4\u5230\u60a8\u5c06 API \u6258\u7ba1\u5728\u4e0e\u9700\u8981\u8bbf\u95ee\u5b83\u7684\u5e94\u7528\u7a0b\u5e8f\u4e0d\u540c\u7684\u57df\u4e0a\u3002<\/p>\n<p>Adding CORS support to your application requires you to do four things:<br \/>\n\u5411\u5e94\u7528\u7a0b\u5e8f\u6dfb\u52a0 CORS \u652f\u6301\u9700\u8981\u60a8\u6267\u884c\u4ee5\u4e0b\u56db\u9879\u4f5c\uff1a<\/p>\n<p>\u2022  Add the CORS services to your app.<br \/>\n\u5c06 CORS \u670d\u52a1\u6dfb\u52a0\u5230\u5e94\u7528\u7a0b\u5e8f\u3002<\/p>\n<p>\u2022  Con\ufb01gure at least one CORS policy.<br \/>\n\u81f3\u5c11\u914d\u7f6e\u4e00\u4e2a CORS \u7b56\u7565\u3002<\/p>\n<p>\u2022  Add the CORS middleware to your middleware pipeline.<br \/>\n\u5c06 CORS \u4e2d\u95f4\u4ef6\u6dfb\u52a0\u5230\u60a8\u7684\u4e2d\u95f4\u4ef6\u7ba1\u9053\u4e2d\u3002<\/p>\n<p>\u2022  Set a default CORS policy for your entire app or decorate your endpoints with EnableCors metadata to selectively enable CORS for speci\ufb01c endpoints.<br \/>\n\u4e3a\u6574\u4e2a\u5e94\u7528\u7a0b\u5e8f\u8bbe\u7f6e\u9ed8\u8ba4 CORS \u7b56\u7565\uff0c\u6216\u4f7f\u7528 EnableCors \u5143\u6570\u636e\u88c5\u9970\u7ec8\u7aef\u8282\u70b9\uff0c\u4ee5\u9009\u62e9\u6027\u5730\u4e3a\u7279\u5b9a\u7ec8\u7aef\u8282\u70b9\u542f\u7528 CORS\u3002<\/p>\n<p>To add the CORS services to your application, call AddCors() on your WebApplicationBuilder instance in Program.cs:<br \/>\n\u8981\u5c06 CORS \u670d\u52a1\u6dfb\u52a0\u5230\u5e94\u7528\u7a0b\u5e8f\u4e2d\uff0c\u8bf7\u5728 Program.cs \u4e2d\u7684 WebApplicationBuilder \u5b9e\u4f8b\u4e0a\u8c03\u7528 AddCors\uff08\uff09\uff1a<\/p>\n<pre><code>builder.Services.AddCors();<\/code><\/pre>\n<p>The bulk of your e\ufb00ort in con\ufb01guring CORS will go into policy con\ufb01guration. A CORS policy controls how your application responds to cross-origin requests. It de\ufb01nes which origins are allowed, which headers to return, which HTTP methods to allow, and so on. You normally de\ufb01ne your policies inline when you add the CORS services to your application.<br \/>\n\u914d\u7f6e CORS \u7684\u5927\u90e8\u5206\u5de5\u4f5c\u5c06\u7528\u4e8e\u7b56\u7565\u914d\u7f6e\u3002CORS \u7b56\u7565\u63a7\u5236\u5e94\u7528\u7a0b\u5e8f\u5982\u4f55\u54cd\u5e94\u8de8\u57df\u8bf7\u6c42\u3002\u5b83\u5b9a\u4e49\u5141\u8bb8\u54ea\u4e9b\u6e90\u3001\u8981\u8fd4\u56de\u54ea\u4e9b\u6807\u5934\u3001\u5141\u8bb8\u54ea\u4e9b HTTP \u65b9\u6cd5\u7b49\u3002\u901a\u5e38\u5728\u5c06 CORS \u670d\u52a1\u6dfb\u52a0\u5230\u5e94\u7528\u7a0b\u5e8f\u65f6\uff0c\u4ee5\u5185\u8054\u65b9\u5f0f\u5b9a\u4e49\u7b56\u7565\u3002<\/p>\n<p>Consider the previous e-commerce site example. You want your API that is hosted at <a href=\"http:\/\/api.shopping.com\">http:\/\/api.shopping.com<\/a> to be available from the main app via client-side JavaScript, hosted at <a href=\"http:\/\/shopping.com\">http:\/\/shopping.com<\/a>. You therefore need to con\ufb01gure the API to allow cross-origin requests.<br \/>\n\u8003\u8651\u524d\u9762\u7684\u7535\u5b50\u5546\u52a1\u7f51\u7ad9\u793a\u4f8b\u3002\u60a8\u5e0c\u671b\u6258\u7ba1\u5728 <a href=\"http:\/\/api.shopping.com\">http:\/\/api.shopping.com<\/a> \u7684 API \u53ef\u4ee5\u901a\u8fc7\u6258\u7ba1\u5728 <a href=\"http:\/\/shopping.com\">http:\/\/shopping.com<\/a> \u7684\u5ba2\u6237\u7aef JavaScript \u4ece\u4e3b\u5e94\u7528\u7a0b\u5e8f\u8bbf\u95ee\u3002\u56e0\u6b64\uff0c\u60a8\u9700\u8981\u914d\u7f6e API \u4ee5\u5141\u8bb8\u8de8\u57df\u8bf7\u6c42\u3002<\/p>\n<p><b>NOTE<\/b>  Remember, it\u2019s the app that will get errors when attempting to make cross-origin requests, but it\u2019s the API you\u2019re accessing that you need to add CORS to, not the app making the requests.<br \/>\n\u6ce8\u610f:\u8bf7\u8bb0\u4f4f\uff0c\u5728\u5c1d\u8bd5\u53d1\u51fa\u8de8\u57df\u8bf7\u6c42\u65f6\uff0c\u5e94\u7528\u7a0b\u5e8f\u4f1a\u9047\u5230\u9519\u8bef\uff0c\u4f46\u9700\u8981\u5c06 CORS \u6dfb\u52a0\u5230\u60a8\u6b63\u5728\u8bbf\u95ee\u7684 API \u4e0a\uff0c\u800c\u4e0d\u662f\u53d1\u51fa\u8bf7\u6c42\u7684\u5e94\u7528\u7a0b\u5e8f\u3002<\/p>\n<p>The following listing shows how to con\ufb01gure a policy called &quot;AllowShoppingApp&quot; to enable cross-origin requests from <a href=\"http:\/\/shopping.com\">http:\/\/shopping.com<\/a> to the API. Additionally, we explicitly allow any HTTP verb type; without this call, only simple methods (GET, HEAD, and POST) are allowed. The policies are built up using the familiar \ufb02uent builder style you\u2019ve seen throughout this book.<br \/>\n\u4ee5\u4e0b\u6e05\u5355\u663e\u793a\u4e86\u5982\u4f55\u914d\u7f6e\u4e00\u4e2a\u540d\u4e3a \u201cAllowShoppingApp\u201d \u7684\u7b56\u7565\uff0c\u4ee5\u542f\u7528\u4ece <a href=\"http:\/\/shopping.com\">http:\/\/shopping.com<\/a> \u5230 API \u7684\u8de8\u57df\u8bf7\u6c42\u3002\u6b64\u5916\uff0c\u6211\u4eec\u660e\u786e\u5141\u8bb8\u4efb\u4f55 HTTP \u52a8\u8bcd\u7c7b\u578b;\u5982\u679c\u6ca1\u6709\u6b64\u8c03\u7528\uff0c\u5219\u53ea\u5141\u8bb8\u4f7f\u7528\u7b80\u5355\u7684\u65b9\u6cd5 \uff08GET\u3001HEAD \u548c POST\uff09 \u3002\u8fd9\u4e9b\u7b56\u7565\u662f\u4f7f\u7528\u60a8\u5728\u672c\u4e66\u4e2d\u770b\u5230\u7684\u719f\u6089\u7684 Fluent Builder \u98ce\u683c\u6784\u5efa\u7684\u3002<\/p>\n<p>Listing 29.2 Con\ufb01guring a CORS policy to allow requests from a speci\ufb01c origin<br \/>\n\u793a\u4f8b 29.2 \u914d\u7f6e CORS \u7b56\u7565\u4ee5\u5141\u8bb8\u6765\u81ea\u7279\u5b9a\u6e90\u7684\u8bf7\u6c42<\/p>\n<pre><code>public void ConfigureServices(IServiceCollection services)\n{\nservices.AddCors(options =&gt; { \u2776\noptions.AddPolicy(&quot;AllowShoppingApp&quot;, policy =&gt; \u2777\npolicy.WithOrigins(&quot;http:\/\/shopping.com&quot;) \u2778\n.AllowAnyMethod()); \u2779\n});\n\/\/ other service configuration\n}<\/code><\/pre>\n<p>\u2776 The AddCors method exposes an <code>Action&lt;CorsOptions&gt;<\/code> overload.<br \/>\nAddCors \u65b9\u6cd5\u516c\u5f00<code>Action&lt;CorsOptions&gt;<\/code> \u91cd\u8f7d\u3002<\/p>\n<p>\u2777 Every policy has a unique name.<br \/>\n\u6bcf\u4e2a\u7b56\u7565\u90fd\u6709\u4e00\u4e2a\u552f\u4e00\u7684\u540d\u79f0\u3002<\/p>\n<p>\u2778 The WithOrigins method specifies which origins are allowed. Note that the URL has no trailing \/.<br \/>\nWithOrigins \u65b9\u6cd5\u6307\u5b9a\u5141\u8bb8\u7684\u6e90\u3002\u8bf7\u6ce8\u610f\uff0c\u8be5 URL \u6ca1\u6709\u5c3e\u90e8 \/\u3002<\/p>\n<p>\u2779 Allows all HTTP verbs to call the API<br \/>\n\u5141\u8bb8\u6240\u6709 HTTP \u52a8\u8bcd\u8c03\u7528 API<\/p>\n<p><b>WARNING<\/b> When listing origins in WithOrigins(), ensure that they don\u2019t have a trailing &quot;\/&quot;; otherwise, the origin will never match, and your cross-origin requests will fail.<br \/>\n\u8b66\u544a:\u5728 WithOrigins\uff08\uff09 \u4e2d\u5217\u51fa\u6e90\u65f6\uff0c\u8bf7\u786e\u4fdd\u5b83\u4eec\u6ca1\u6709\u5c3e\u968f\u7684 \u201c\/\u201d;\u5426\u5219\uff0c\u6e90\u5c06\u6c38\u8fdc\u4e0d\u4f1a\u5339\u914d\uff0c\u5e76\u4e14\u60a8\u7684\u8de8\u6e90\u8bf7\u6c42\u5c06\u5931\u8d25\u3002<\/p>\n<p>Once you\u2019ve de\ufb01ned a CORS policy, you can apply it to your application. In the following listing, you apply the &quot;AllowShoppingApp&quot; policy to the whole application using CorsMiddleware by calling UseCors().<br \/>\n\u5b9a\u4e49 CORS \u7b56\u7565\u540e\uff0c\u60a8\u53ef\u4ee5\u5c06\u5176\u5e94\u7528\u4e8e\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u3002\u5728\u4e0b\u9762\u7684\u6e05\u5355\u4e2d\uff0c\u901a\u8fc7\u8c03\u7528 UseCors\uff08\uff09 \u4f7f\u7528 CorsMiddleware \u5c06 \u201cAllowShoppingApp\u201d \u7b56\u7565\u5e94\u7528\u4e8e\u6574\u4e2a\u5e94\u7528\u7a0b\u5e8f\u3002<\/p>\n<p>Listing 29.3 Adding the CORS middleware and con\ufb01guring a default CORS policy<br \/>\n\u6e05\u5355 29.3 \u6dfb\u52a0 CORS \u4e2d\u95f4\u4ef6\u5e76\u914d\u7f6e\u9ed8\u8ba4 CORS \u7b56\u7565<\/p>\n<pre><code>var builder = WebApplication.CreateBuilder(args);\nbuilder.Services.AddCors(options =&gt; {\noptions.AddPolicy(&quot;AllowShoppingApp&quot;, policy =&gt;\npolicy.WithOrigins(&quot;http:\/\/shopping.com&quot;)\n.AllowAnyMethod());\n});\nvar app = builder.Build();\napp.UseRouting();\napp.UseCors(&quot;AllowShoppingApp&quot;); \u2776\napp.UseAuthentication();\napp.UseAuthorization();\napp.MapGet(&quot;\/api\/products&quot;, () =&gt; new string[] {});\napp.Run();<\/code><\/pre>\n<p>\u2776 Adds the CORS middleware and uses AllowShoppingApp as the default policy<br \/>\n\u6dfb\u52a0 CORS \u4e2d\u95f4\u4ef6\u5e76\u4f7f\u7528 AllowShoppingApp \u4f5c\u4e3a\u9ed8\u8ba4\u7b56\u7565<\/p>\n<p><b>NOTE<\/b>  As with all middleware, the order of the CORS middleware is important. You must place the call to UseCors() after UseRouting(). The CORS middleware needs to intercept cross-origin requests to your web API actions so it can generate the correct responses to pre\ufb02ight requests and add the necessary headers. It\u2019s common to place the CORS middleware before a call to UseAuthentication().<br \/>\n\u6ce8\u610f:\u4e0e\u6240\u6709\u4e2d\u95f4\u4ef6\u4e00\u6837\uff0cCORS \u4e2d\u95f4\u4ef6\u7684\u987a\u5e8f\u4e5f\u5f88\u91cd\u8981\u3002\u60a8\u5fc5\u987b\u5728 UseRouting\uff08\uff09 \u4e4b\u540e\u8c03\u7528 UseCors\uff08\uff09\u3002CORS \u4e2d\u95f4\u4ef6\u9700\u8981\u62e6\u622a\u5bf9 Web API\u4f5c\u7684\u8de8\u57df\u8bf7\u6c42\uff0c\u4ee5\u4fbf\u5b83\u53ef\u4ee5\u751f\u6210\u5bf9\u9884\u68c0\u8bf7\u6c42\u7684\u6b63\u786e\u54cd\u5e94\u5e76\u6dfb\u52a0\u5fc5\u8981\u7684\u6807\u5934\u3002\u901a\u5e38\u5c06 CORS \u4e2d\u95f4\u4ef6\u653e\u5728\u8c03\u7528 UseAuthentication\uff08\uff09 \u4e4b\u524d\u3002<\/p>\n<p>With the CORS middleware in place for the API, the shopping app can now make cross-origin requests. You can call the API from the <a href=\"http:\/\/shopping.com\">http:\/\/shopping.com<\/a> site, and the browser lets the CORS request through, as shown in \ufb01gure 29.8. If you make the same request from a domain other than <a href=\"http:\/\/shopping.com\">http:\/\/shopping.com<\/a>, the request continues to be blocked.<br \/>\n\u4e3a API \u90e8\u7f72 CORS \u4e2d\u95f4\u4ef6\u540e\uff0c\u8d2d\u7269\u5e94\u7528\u7a0b\u5e8f\u73b0\u5728\u53ef\u4ee5\u53d1\u51fa\u8de8\u57df\u8bf7\u6c42\u3002\u60a8\u53ef\u4ee5\u4ece <a href=\"http:\/\/shopping.com\">http:\/\/shopping.com<\/a> \u7ad9\u70b9\u8c03\u7528 API\uff0c\u6d4f\u89c8\u5668\u5141\u8bb8 CORS \u8bf7\u6c42\u901a\u8fc7\uff0c\u5982\u56fe 29.8 \u6240\u793a\u3002\u5982\u679c\u60a8\u4ece <a href=\"http:\/\/shopping.com\">http:\/\/shopping.com<\/a> \u4ee5\u5916\u7684\u57df\u53d1\u51fa\u76f8\u540c\u7684\u8bf7\u6c42\uff0c\u8be5\u8bf7\u6c42\u5c06\u7ee7\u7eed\u88ab\u963b\u6b62\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/2908.jpg\" alt=\"alt text\" \/><\/p>\n<p>Figure 29.8 With CORS enabled, as in the bottom image, cross-origin requests can be made, and the browser will make the response available to the JavaScript. Compare this to the top image, in which the request was blocked.<br \/>\n\u56fe 29.8 \u542f\u7528 CORS \u540e\uff0c\u5982\u4e0b\u56fe\u6240\u793a\uff0c\u53ef\u4ee5\u53d1\u51fa\u8de8\u57df\u8bf7\u6c42\uff0c\u5e76\u4e14\u6d4f\u89c8\u5668\u4f1a\u5c06\u54cd\u5e94\u63d0\u4f9b\u7ed9 JavaScript\u3002\u5c06\u6b64\u56fe\u50cf\u4e0e\u8bf7\u6c42\u88ab\u963b\u6b62\u7684\u9876\u90e8\u56fe\u50cf\u8fdb\u884c\u6bd4\u8f83\u3002<\/p>\n<p>Applying a CORS policy globally to your application in this way may be overkill. If there\u2019s only a subset of actions in your API that need to be accessed from other origins, it\u2019s prudent to enable CORS only for those speci\ufb01c actions. This can be achieved by adding metadata to your endpoints.<br \/>\n\u4ee5\u8fd9\u79cd\u65b9\u5f0f\u5c06 CORS \u7b56\u7565\u5168\u5c40\u5e94\u7528\u4e8e\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u53ef\u80fd\u6709\u70b9\u77eb\u6789\u8fc7\u6b63\u3002\u5982\u679c\u60a8\u7684 API \u4e2d\u53ea\u6709\u4e00\u4e2a\u4f5c\u5b50\u96c6\u9700\u8981\u4ece\u5176\u4ed6\u6e90\u8bbf\u95ee\uff0c\u5219\u8c28\u614e\u7684\u505a\u6cd5\u662f\u4ec5\u4e3a\u8fd9\u4e9b\u7279\u5b9a\u4f5c\u542f\u7528 CORS\u3002\u8fd9\u53ef\u4ee5\u901a\u8fc7\u5411\u7ec8\u7aef\u8282\u70b9\u6dfb\u52a0\u5143\u6570\u636e\u6765\u5b9e\u73b0\u3002<\/p>\n<h3>29.3.3 Adding CORS to speci\ufb01c endpoints with EnableCors metadata\u200c<\/h3>\n<p>29.3.3 \u4f7f\u7528 EnableCors \u5143\u6570\u636e\u5c06 CORS \u6dfb\u52a0\u5230\u7279\u5b9a\u7aef\u70b9<\/p>\n<p>Browsers block cross-origin requests by default for good reason: they have the potential to be abused by malicious or compromised sites. Enabling CORS for your entire app may not be worth the risk if you know that only a subset of actions will ever need to be accessed cross-origin.<br \/>\n\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u6d4f\u89c8\u5668\u4f1a\u963b\u6b62\u8de8\u57df\u8bf7\u6c42\uff0c\u8fd9\u662f\u6709\u5145\u5206\u7406\u7531\u7684\uff1a\u5b83\u4eec\u6709\u53ef\u80fd\u88ab\u6076\u610f\u6216\u53d7\u611f\u67d3\u7684\u7f51\u7ad9\u6ee5\u7528\u3002\u5982\u679c\u60a8\u77e5\u9053\u53ea\u9700\u8981\u8de8\u57df\u8bbf\u95ee\u4e00\u90e8\u5206\u4f5c\uff0c\u90a3\u4e48\u4e3a\u6574\u4e2a\u5e94\u7528\u7a0b\u5e8f\u542f\u7528 CORS \u53ef\u80fd\u4e0d\u503c\u5f97\u5192\u9669\u3002<\/p>\n<p>If that\u2019s the case, it\u2019s best to enable a CORS policy only for those speci\ufb01c endpoints. ASP.NET Core provides the RequireCors() method, which you can apply to your minimal API endpoints or route groups, and the [EnableCors] attribute, which lets you select a policy to apply to a given controller or action method.<br \/>\n\u5982\u679c\u662f\u8fd9\u79cd\u60c5\u51b5\uff0c\u6700\u597d\u4ec5\u4e3a\u8fd9\u4e9b\u7279\u5b9a\u7ec8\u7aef\u8282\u70b9\u542f\u7528 CORS \u7b56\u7565\u3002ASP.NET Core \u63d0\u4f9b\u4e86 RequireCors\uff08\uff09 \u65b9\u6cd5\uff08\u53ef\u5e94\u7528\u4e8e\u6700\u5c0f API \u7ec8\u7aef\u8282\u70b9\u6216\u8def\u7531\u7ec4\uff09\u548c [EnableCors] \u5c5e\u6027\uff08\u53ef\u7528\u4e8e\u9009\u62e9\u8981\u5e94\u7528\u4e8e\u7ed9\u5b9a\u63a7\u5236\u5668\u6216\u4f5c\u65b9\u6cd5\u7684\u7b56\u7565\uff09\u3002<\/p>\n<p><b>NOTE<\/b>  Both these methods add CORS metadata to the endpoint, which is used by the CorsMiddleware to determine the policy to apply. This is why the CorsMiddleware should be placed after the RoutingMiddleware, so that the CorsMiddleware knows which endpoint was selected and so which CORS policy to apply.<br \/>\n\u6ce8\u610f:\u8fd9\u4e24\u79cd\u65b9\u6cd5\u90fd\u4f1a\u5c06 CORS \u5143\u6570\u636e\u6dfb\u52a0\u5230\u7ec8\u7aef\u8282\u70b9\uff0cCorsMiddleware \u4f7f\u7528\u8be5\u5143\u6570\u636e\u6765\u786e\u5b9a\u8981\u5e94\u7528\u7684\u7b56\u7565\u3002\u8fd9\u5c31\u662f\u4e3a\u4ec0\u4e48 CorsMiddleware \u5e94\u8be5\u653e\u5728 RoutingMiddleware \u4e4b\u540e\uff0c\u8fd9\u6837 CorsMiddleware \u5c31\u77e5\u9053\u9009\u62e9\u4e86\u54ea\u4e2a\u7aef\u70b9\uff0c\u4ee5\u53ca\u8981\u5e94\u7528\u54ea\u4e2a CORS \u7b56\u7565\u3002<\/p>\n<p>With the RequireCors() method and [EnableCors] attribute, you can apply di\ufb00erent CORS policies to di\ufb00erent endpoints. For example, you could allow GET requests access to your entire API from the <a href=\"http:\/\/shopping.com\">http:\/\/shopping.com<\/a> domain but\u200c allow other HTTP verbs only for a speci\ufb01c endpoint while allowing anyone to access your product list endpoints.<br \/>\n\u4f7f\u7528 RequireCors\uff08\uff09 \u65b9\u6cd5\u548c [EnableCors] \u5c5e\u6027\uff0c\u60a8\u53ef\u4ee5\u5c06\u4e0d\u540c\u7684 CORS \u7b56\u7565\u5e94\u7528\u4e8e\u4e0d\u540c\u7684\u7aef\u70b9\u3002\u4f8b\u5982\uff0c\u60a8\u53ef\u4ee5\u5141\u8bb8 GET \u8bf7\u6c42\u4ece <a href=\"http:\/\/shopping.com\">http:\/\/shopping.com<\/a> \u57df\u8bbf\u95ee\u60a8\u7684\u6574\u4e2a API\uff0c\u4f46\u4ec5\u5141\u8bb8\u7279\u5b9a\u7ec8\u7aef\u8282\u70b9\u4f7f\u7528\u5176\u4ed6 HTTP \u52a8\u8bcd\uff0c\u540c\u65f6\u5141\u8bb8\u4efb\u4f55\u4eba\u8bbf\u95ee\u60a8\u7684\u4ea7\u54c1\u5217\u8868\u7ec8\u7aef\u8282\u70b9\u3002<\/p>\n<p>You de\ufb01ne CORS policies in the call to AddCors() by calling AddPolicy() and giving the policy a name, as you saw in listing 29.2. If you\u2019re using endpoint-speci\ufb01c policies, instead of calling UseCors(&quot;AllowShoppingApp&quot;) as you saw in listing 29.3, you should add the middleware without a default policy by calling UseCors() only.<br \/>\n\u901a\u8fc7\u8c03\u7528 AddPolicy\uff08\uff09 \u5e76\u4e3a\u7b56\u7565\u547d\u540d\uff0c\u60a8\u53ef\u4ee5\u5728\u5bf9 AddCors\uff08\uff09 \u7684\u8c03\u7528\u4e2d\u5b9a\u4e49 CORS \u7b56\u7565\uff0c\u5982\u6e05\u5355 29.2 \u6240\u793a\u3002\u5982\u679c\u60a8\u4f7f\u7528\u7684\u662f\u7279\u5b9a\u4e8e\u7aef\u70b9\u7684\u7b56\u7565\uff0c\u800c\u4e0d\u662f\u50cf\u60a8\u5728\u6e05\u5355 29.3 \u4e2d\u770b\u5230\u7684\u90a3\u6837\u8c03\u7528 UseCors\uff08\u201cAllowShoppingApp\u201d\uff09\uff0c\u60a8\u5e94\u8be5\u4ec5\u901a\u8fc7\u8c03\u7528 UseCors\uff08\uff09 \u6765\u6dfb\u52a0\u6ca1\u6709\u9ed8\u8ba4\u7b56\u7565\u7684\u4e2d\u95f4\u4ef6\u3002<\/p>\n<p>You can then selectively enable CORS for individual endpoints and specifying the policy to apply. To apply CORS to a minimal API endpoint or route group, call RequireCors(&quot;AllowShoppingApp&quot;), as shown in the following listing. To apply a policy to a controller or an action method, apply the [EnableCors(&quot;AllowShoppingApp&quot;] attribute. You can disable cross-origin access for an endpoint by applying the [DisableCors] attribute.<br \/>\n\u7136\u540e\uff0c\u60a8\u53ef\u4ee5\u6709\u9009\u62e9\u5730\u4e3a\u5355\u4e2a\u7ec8\u7aef\u8282\u70b9\u542f\u7528 CORS \u5e76\u6307\u5b9a\u8981\u5e94\u7528\u7684\u7b56\u7565\u3002\u8981\u5c06 CORS \u5e94\u7528\u4e8e\u6700\u5c0f API \u7ec8\u7aef\u8282\u70b9\u6216\u8def\u7531\u7ec4\uff0c\u8bf7\u8c03\u7528 RequireCors\uff08\u201cAllowShoppingApp\u201d\uff09\uff0c\u5982\u4e0b\u9762\u7684\u6e05\u5355\u6240\u793a\u3002\u8981\u5c06\u7b56\u7565\u5e94\u7528\u4e8e\u63a7\u5236\u5668\u6216\u4f5c\u65b9\u6cd5\uff0c\u8bf7\u5e94\u7528 [EnableCors\uff08\u201cAllowShoppingApp\u201d] \u5c5e\u6027\u3002\u60a8\u53ef\u4ee5\u901a\u8fc7\u5e94\u7528 [DisableCors] \u5c5e\u6027\u6765\u7981\u7528\u7ec8\u7aef\u8282\u70b9\u7684\u8de8\u57df\u8bbf\u95ee\u3002<\/p>\n<p>Listing 29.4 Applying a CORS policy to minimal API endpoints<br \/>\n\u6e05\u5355 29.4 \u5c06 CORS \u7b56\u7565\u5e94\u7528\u4e8e\u6700\u5c0f\u7684 API \u7aef\u70b9<\/p>\n<pre><code>WebApplicationBuilder builder = WebApplication.CreateBuilder(args);\nbuilder.Services.AddCors(options =&gt; { \/* Config not shown*\/});\nvar app = builder.Build();\napp.UseCors(); \u2776\napp.MapGet(&quot;\/api\/products&quot;, () =&gt; new string[] {})\n.RequireCors(&quot;AllowShoppingApp&quot;); \u2777\napp.MapGet(&quot;\/api\/products&quot;,\n[EnableCors(&quot;AllowShoppingApp&quot;)] () =&gt; new { }); \u2778\napp.MapGroup(&quot;\/api\/categories&quot;)\n.RequireCors(&quot;AllowAnyOrigin&quot;); \u2779\napp.MapDelete(&quot;\/api\/products&quot;,\n[DisableCors] () =&gt; Results.NoContent()); \u277a\napp.Run();<\/code><\/pre>\n<p>\u2776 Adds the CorsMiddleware without configuring a default policy<br \/>\n\u6dfb\u52a0 CorsMiddleware \u800c\u4e0d\u914d\u7f6e\u9ed8\u8ba4\u7b56\u7565<\/p>\n<p>\u2777 Applies the AllowShoppingApp CORS policy to the endpoint<br \/>\n\u5c06 AllowShoppingApp CORS \u7b56\u7565\u5e94\u7528\u4e8e\u7ec8\u7aef\u8282\u70b9<\/p>\n<p>\u2778 You can apply attributes to the lamba or handler method, as well as to MVC action methods.<br \/>\n\u60a8\u53ef\u4ee5\u5c06\u5c5e\u6027\u5e94\u7528\u4e8e lamba \u6216\u5904\u7406\u7a0b\u5e8f\u65b9\u6cd5\uff0c\u4ee5\u53ca MVC\u52a8\u4f5c\u65b9\u6cd5\u3002<\/p>\n<p>\u2779 You can apply CORS policies to whole route groups.<br \/>\n\u60a8\u53ef\u4ee5\u5c06 CORS \u7b56\u7565\u5e94\u7528\u4e8e\u6574\u4e2a\u8def\u7531\u7ec4\u3002<\/p>\n<p>\u277a The DisableCors attribute disables CORS for the endpoint completely.<br \/>\nDisableCors \u5c5e\u6027\u5b8c\u5168\u7981\u7528\u7ec8\u7aef\u8282\u70b9\u7684 CORS\u3002<\/p>\n<p>If you de\ufb01ne a default policy but then also call RequireCors() or add an [EnableCors] attribute, then both policies are applied. This can get confusing, so I recommend not applying a default CORS policy in the middleware and specifying the policy at the route group or endpoint level. Alternatively, if you do want to apply a policy to your whole app, avoid applying individual policies to endpoints as well.<br \/>\n\u5982\u679c\u5b9a\u4e49\u4e86\u9ed8\u8ba4\u7b56\u7565\uff0c\u4f46\u968f\u540e\u8fd8\u8c03\u7528 RequireCors\uff08\uff09 \u6216\u6dfb\u52a0 [EnableCors] \u5c5e\u6027\uff0c\u5219\u4f1a\u5e94\u7528\u8fd9\u4e24\u4e2a\u7b56\u7565\u3002\u8fd9\u53ef\u80fd\u4f1a\u9020\u6210\u6df7\u6dc6\uff0c\u56e0\u6b64\u6211\u5efa\u8bae\u4e0d\u8981\u5728\u4e2d\u95f4\u4ef6\u4e2d\u5e94\u7528\u9ed8\u8ba4 CORS \u7b56\u7565\uff0c\u800c\u662f\u5728\u8def\u7531\u7ec4\u6216\u7ec8\u7aef\u8282\u70b9\u7ea7\u522b\u6307\u5b9a\u7b56\u7565\u3002\u6216\u8005\uff0c\u5982\u679c\u60a8\u786e\u5b9e\u5e0c\u671b\u5c06\u7b56\u7565\u5e94\u7528\u4e8e\u6574\u4e2a\u5e94\u7528\u7a0b\u5e8f\uff0c\u8bf7\u907f\u514d\u5c06\u5355\u4e2a\u7b56\u7565\u4e5f\u5e94\u7528\u4e8e\u7ec8\u7aef\u8282\u70b9\u3002<\/p>\n<p>Whether you choose to use a single default CORS policy or multiple policies, you need to con\ufb01gure the CORS policies for your application in the call to AddCors. Many options are available when con\ufb01guring CORS. In the next section I provide an overview of the possibilities.<br \/>\n\u65e0\u8bba\u60a8\u9009\u62e9\u4f7f\u7528\u5355\u4e2a\u9ed8\u8ba4 CORS \u7b56\u7565\u8fd8\u662f\u591a\u4e2a\u7b56\u7565\uff0c\u90fd\u9700\u8981\u5728\u5bf9 AddCors \u7684\u8c03\u7528\u4e2d\u4e3a\u5e94\u7528\u7a0b\u5e8f\u914d\u7f6e CORS \u7b56\u7565\u3002\u914d\u7f6e CORS \u65f6\uff0c\u6709\u8bb8\u591a\u9009\u9879\u53ef\u7528\u3002\u5728\u4e0b\u4e00\u8282\u4e2d\uff0c\u6211\u5c06\u6982\u8ff0\u5404\u79cd\u53ef\u80fd\u6027\u3002<\/p>\n<h3>29.3.4 Con\ufb01guring CORS policies\u200c<\/h3>\n<p>29.3.4 \u914d\u7f6e CORS \u7b56\u7565<\/p>\n<p>Browsers implement the cross-origin policy for security reasons, so you should carefully consider the implications of relaxing any of the restrictions they impose. Even if you enable cross-origin requests, you can still control what data cross-origin requests can send and what your API returns. For example, you can con\ufb01gure<br \/>\n\u6d4f\u89c8\u5668\u51fa\u4e8e\u5b89\u5168\u539f\u56e0\u5b9e\u65bd\u8de8\u57df\u7b56\u7565\uff0c\u56e0\u6b64\u60a8\u5e94\u8be5\u4ed4\u7ec6\u8003\u8651\u653e\u5bbd\u5b83\u4eec\u65bd\u52a0\u7684\u4efb\u4f55\u9650\u5236\u7684\u5f71\u54cd\u3002\u5373\u4f7f\u60a8\u542f\u7528\u4e86\u8de8\u57df\u8bf7\u6c42\uff0c\u60a8\u4ecd\u7136\u53ef\u4ee5\u63a7\u5236\u8de8\u57df\u8bf7\u6c42\u53ef\u4ee5\u53d1\u9001\u7684\u6570\u636e\u4ee5\u53ca API \u8fd4\u56de\u7684\u6570\u636e\u3002\u4f8b\u5982\uff0c\u60a8\u53ef\u4ee5\u914d\u7f6e<\/p>\n<p>\u2022  The origins that may make a cross-origin request to your API<br \/>\n\u53ef\u80fd\u5411\u60a8\u7684 API \u53d1\u51fa\u8de8\u6e90\u8bf7\u6c42\u7684\u6e90<\/p>\n<p>\u2022  The HTTP verbs (such as GET, POST, and DELETE) that can be used<br \/>\n\u53ef\u4ee5\u4f7f\u7528\u7684 HTTP \u52a8\u8bcd \uff08\u5982 GET\u3001POST \u548c DELETE\uff09<\/p>\n<p>\u2022  The headers the browser can send<br \/>\n\u6d4f\u89c8\u5668\u53ef\u4ee5\u53d1\u9001\u7684\u6807\u5934<\/p>\n<p>\u2022  The headers the browser can read from your app\u2019s response<br \/>\n\u6d4f\u89c8\u5668\u53ef\u4ee5\u4ece\u5e94\u7528\u7684\u54cd\u5e94\u4e2d\u8bfb\u53d6\u7684\u6807\u5934<\/p>\n<p>\u2022  Whether the browser will send authentication credentials with the request<br \/>\n\u6d4f\u89c8\u5668\u662f\u5426\u4f1a\u968f\u8bf7\u6c42\u53d1\u9001\u8eab\u4efd\u9a8c\u8bc1\u51ed\u8bc1<\/p>\n<p>You de\ufb01ne all these options when creating a CORS policy in your call to AddCors() using the CorsPolicyBuilder, as you saw in listing 29.2. A policy can set all or none of these options, so you can customize the results to your heart\u2019s content. Table 29.1 shows some of the options available and their e\ufb00ects.<br \/>\n\u4f7f\u7528 CorsPolicyBuilder \u5728\u8c03\u7528 AddCors\uff08\uff09 \u4e2d\u521b\u5efa CORS \u7b56\u7565\u65f6\uff0c\u60a8\u53ef\u4ee5\u5b9a\u4e49\u6240\u6709\u8fd9\u4e9b\u9009\u9879\uff0c\u5982\u6e05\u5355 29.2 \u6240\u793a\u3002\u7b56\u7565\u53ef\u4ee5\u8bbe\u7f6e\u6240\u6709\u8fd9\u4e9b\u9009\u9879\uff0c\u4e5f\u53ef\u4ee5\u4e0d\u8bbe\u7f6e\u8fd9\u4e9b\u9009\u9879\uff0c\u56e0\u6b64\u60a8\u53ef\u4ee5\u6839\u636e\u81ea\u5df1\u7684\u559c\u597d\u81ea\u5b9a\u4e49\u7ed3\u679c\u3002Table 29.1 \u663e\u793a\u4e86\u4e00\u4e9b\u53ef\u7528\u7684\u9009\u9879\u53ca\u5176\u6548\u679c\u3002<\/p>\n<p>Table 29.1 The methods available for con\ufb01guring a CORS policy and their e\ufb00ect on the policy<br \/>\n\u8868 29.1 \u53ef\u7528\u4e8e\u914d\u7f6e CORS \u7b56\u7565\u7684\u65b9\u6cd5\u53ca\u5176\u5bf9\u7b56\u7565\u7684\u5f71\u54cd<\/p>\n<table>\n<thead>\n<tr>\n<th>CorsPolicyBuilder method example<\/th>\n<th>Result<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>WithOrigins(&quot;<a href=\"http:\/\/shopping.com\">http:\/\/shopping.com<\/a>&quot;)<\/td>\n<td>Allows cross-origin requests from http:\/ \/shopping.com <br \/>\u5141\u8bb8\u6765\u81ea http\uff1a\/ \/shopping.com \u7684\u8de8\u57df\u8bf7\u6c42<\/td>\n<\/tr>\n<tr>\n<td>AllowAnyOrigin()<\/td>\n<td>Allows cross-origin requests from any origin. This means any website can make JavaScript requests to your API.  <br \/> \u5141\u8bb8\u6765\u81ea\u4efb\u4f55\u6e90\u7684\u8de8\u57df\u8bf7\u6c42\u3002\u8fd9\u610f\u5473\u7740\u4efb\u4f55\u7f51\u7ad9\u90fd\u53ef\u4ee5\u5411\u60a8\u7684 API \u53d1\u51fa JavaScript \u8bf7\u6c42\u3002<\/td>\n<\/tr>\n<tr>\n<td>WithMethods()\/AllowAnyMethod()<\/td>\n<td>Sets the allowed methods (such as GET, POST, and DELETE) that can be made to your API <br \/>  \u8bbe\u7f6e\u5141\u8bb8\u5bf9 API \u8fdb\u884c\u7684\u65b9\u6cd5\uff08\u4f8b\u5982 GET\u3001POST \u548c DELETE\uff09<\/td>\n<\/tr>\n<tr>\n<td>WithHeaders()\/AllowAnyHeader()<\/td>\n<td>Sets the headers that the browser may send to your API. If you restrict the headers, you must include at least Accept, Content-Type, and Origin to allow valid requests. <br \/>  \u8bbe\u7f6e\u6d4f\u89c8\u5668\u53ef\u4ee5\u53d1\u9001\u5230 API \u7684\u6807\u5934\u3002\u5982\u679c\u60a8\u9650\u5236\u6807\u5934\uff0c\u5219\u5fc5\u987b\u81f3\u5c11\u5305\u542b Accept\u3001Content-Type \u548c Origin \u624d\u80fd\u5141\u8bb8\u6709\u6548\u8bf7\u6c42\u3002<\/td>\n<\/tr>\n<tr>\n<td>WithExposedHeaders()<\/td>\n<td>Allows your API to send extra headers to the browser. By default, only the Cache-Control, Content-Language,Content-Type, Expires, Last-Modified,and Pragma headers are sent in the response. <br \/> \u5141\u8bb8 API \u5411\u6d4f\u89c8\u5668\u53d1\u9001\u989d\u5916\u7684\u6807\u5934\u3002\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u54cd\u5e94\u4e2d\u4ec5\u53d1\u9001 Cache-Control\u3001Content-Language\u3001Content-Type\u3001Expires\u3001Last-Modified \u548c Pragma \u6807\u5934\u3002<\/td>\n<\/tr>\n<tr>\n<td>AllowCredentials()<\/td>\n<td>By default, the browser won\u2019t send authentication details with cross- origin requests unless you explicitly allow it. You must also enable sending credentials client-side in JavaScript when making the request. <br \/> \u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u9664\u975e\u60a8\u660e\u786e\u5141\u8bb8\uff0c\u5426\u5219\u6d4f\u89c8\u5668\u4e0d\u4f1a\u901a\u8fc7\u8de8\u57df\u8bf7\u6c42\u53d1\u9001\u8eab\u4efd\u9a8c\u8bc1\u8be6\u7ec6\u4fe1\u606f\u3002\u53d1\u51fa\u8bf7\u6c42\u65f6\uff0c\u60a8\u8fd8\u5fc5\u987b\u5728 JavaScript \u4e2d\u542f\u7528\u5ba2\u6237\u7aef\u53d1\u9001\u51ed\u8bc1\u3002<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>One of the \ufb01rst problems in setting up CORS is realizing you have a cross-origin problem at all. Several times I\u2019ve been stumped trying to \ufb01gure out why a request won\u2019t work, until I realize the request is going cross-domain or from HTTP to HTTPS, for example.<br \/>\n\u8bbe\u7f6e CORS \u7684\u9996\u8981\u95ee\u9898\u4e4b\u4e00\u662f\u610f\u8bc6\u5230\u60a8\u6839\u672c\u5b58\u5728\u8de8\u57df\u95ee\u9898\u3002\u6709\u597d\u51e0\u6b21\uff0c\u6211\u8bd5\u56fe\u5f04\u6e05\u695a\u4e3a\u4ec0\u4e48\u4e00\u4e2a\u8bf7\u6c42\u4e0d\u8d77\u4f5c\u7528\uff0c\u76f4\u5230\u6211\u610f\u8bc6\u5230\u8bf7\u6c42\u662f\u8de8\u57df\u7684\uff0c\u6216\u8005\u4ece HTTP \u5230 HTTPS\uff0c\u4f8b\u5982\u3002<\/p>\n<p>Whenever possible, I recommend avoiding cross-origin requests. You can end up with subtle di\ufb00erences in the way browsers handle them, which can cause more headaches. In particular, avoid HTTP to HTTPS cross-domain problems by running all your applications behind HTTPS. As discussed in chapter 28, that\u2019s a best practice anyway, and it\u2019ll help prevent a whole class of CORS headaches.<br \/>\n\u6211\u5efa\u8bae\u5c3d\u53ef\u80fd\u907f\u514d\u8de8\u6e90\u8bf7\u6c42\u3002\u60a8\u6700\u7ec8\u53ef\u80fd\u4f1a\u5728\u6d4f\u89c8\u5668\u5904\u7406\u5b83\u4eec\u7684\u65b9\u5f0f\u4e0a\u4ea7\u751f\u7ec6\u5fae\u7684\u5dee\u5f02\uff0c\u8fd9\u53ef\u80fd\u4f1a\u5bfc\u81f4\u66f4\u591a\u9ebb\u70e6\u3002\u7279\u522b\u662f\uff0c\u901a\u8fc7\u5728 HTTPS \u540e\u9762\u8fd0\u884c\u6240\u6709\u5e94\u7528\u7a0b\u5e8f\u6765\u907f\u514d HTTP \u5230 HTTPS \u7684\u8de8\u57df\u95ee\u9898\u3002\u6b63\u5982\u7b2c 28 \u7ae0\u6240\u8ba8\u8bba\u7684\uff0c\u65e0\u8bba\u5982\u4f55\uff0c\u8fd9\u90fd\u662f\u6700\u4f73\u5b9e\u8df5\uff0c\u5b83\u5c06\u6709\u52a9\u4e8e\u9632\u6b62\u4e00\u6574\u7c7b CORS \u5934\u75db\u3002<\/p>\n<p><b>TIP<\/b>  Another (often preferable) option is to con\ufb01gure CORS policies in your reverse proxy or application gateway. You can con\ufb01gure Azure App Service with allowed origins, for example, so that you don\u2019t need to modify your application code.<br \/>\n\u63d0\u793a:\u53e6\u4e00\u4e2a\uff08\u901a\u5e38\u66f4\u53ef\u53d6\u7684\uff09\u9009\u9879\u662f\u5728\u53cd\u5411\u4ee3\u7406\u6216\u5e94\u7528\u7a0b\u5e8f\u7f51\u5173\u4e2d\u914d\u7f6e CORS \u7b56\u7565\u3002\u4f8b\u5982\uff0c\u53ef\u4ee5\u4f7f\u7528\u5141\u8bb8\u7684\u6e90\u914d\u7f6e Azure \u5e94\u7528\u670d\u52a1\uff0c\u8fd9\u6837\u5c31\u65e0\u9700\u4fee\u6539\u5e94\u7528\u7a0b\u5e8f\u4ee3\u7801\u3002<\/p>\n<p>Once I\u2019ve established that I de\ufb01nitely need a CORS policy, I typically start with the WithOrigins() method. Then I expand or restrict the policy further, as need be, to provide cross-origin lockdown of my API while still allowing the required functionality. CORS can be tricky to work around, but remember, the restrictions are there for your safety.<br \/>\n\u4e00\u65e6\u6211\u786e\u5b9a\u6211\u80af\u5b9a\u9700\u8981\u4e00\u4e2a CORS \u7b56\u7565\uff0c\u6211\u901a\u5e38\u4ece WithOrigins\uff08\uff09 \u65b9\u6cd5\u5f00\u59cb\u3002\u7136\u540e\uff0c\u6211\u6839\u636e\u9700\u8981\u8fdb\u4e00\u6b65\u6269\u5c55\u6216\u9650\u5236\u7b56\u7565\uff0c\u4ee5\u63d0\u4f9b API \u7684\u8de8\u57df\u9501\u5b9a\uff0c\u540c\u65f6\u4ecd\u7136\u5141\u8bb8\u6240\u9700\u7684\u529f\u80fd\u3002CORS \u53ef\u80fd\u5f88\u96be\u89e3\u51b3\uff0c\u4f46\u8bf7\u8bb0\u4f4f\uff0c\u8fd9\u4e9b\u9650\u5236\u662f\u4e3a\u4e86\u60a8\u7684\u5b89\u5168\u3002<\/p>\n<p>Cross-origin requests are only one of many potential avenues attackers could use to compromise your app. Many of these are trivial to defend against, but you need to be aware of them and know how to mitigate them. In the next section we\u2019ll look at common threats and how to avoid them.<br \/>\n\u8de8\u57df\u8bf7\u6c42\u53ea\u662f\u653b\u51fb\u8005\u53ef\u80fd\u7528\u6765\u7834\u574f\u60a8\u7684\u5e94\u7528\u7684\u4f17\u591a\u6f5c\u5728\u9014\u5f84\u4e4b\u4e00\u3002\u5176\u4e2d\u8bb8\u591a\u662f\u5fae\u4e0d\u8db3\u9053\u7684\u9632\u5fa1\uff0c\u4f46\u60a8\u9700\u8981\u4e86\u89e3\u5b83\u4eec\u5e76\u77e5\u9053\u5982\u4f55\u51cf\u8f7b\u5b83\u4eec\u3002\u5728\u4e0b\u4e00\u8282\u4e2d\uff0c\u6211\u4eec\u5c06\u4ecb\u7ecd\u5e38\u89c1\u7684\u5a01\u80c1\u4ee5\u53ca\u5982\u4f55\u907f\u514d\u5b83\u4eec\u3002<\/p>\n<h2>29.4 Exploring other attack vectors\u200c<\/h2>\n<p>29.4 \u63a2\u7d22\u5176\u4ed6\u653b\u51fb\u5a92\u4ecb<\/p>\n<p>So far in this chapter, I\u2019ve described two potential ways attackers can compromise your apps\u2014XSS and CSRF attacks and how to prevent them. Both of these vulnerabilities regularly appear in the OWASP top ten list of most critical web app risks, so it\u2019s important to be aware of them and to avoid introducing them into your apps.<br \/>\n\u5230\u76ee\u524d\u4e3a\u6b62\uff0c\u5728\u672c\u7ae0\u4e2d\uff0c\u6211\u5df2\u7ecf\u4ecb\u7ecd\u4e86\u653b\u51fb\u8005\u7834\u574f\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u7684\u4e24\u79cd\u6f5c\u5728\u65b9\u5f0f \u2014 XSS \u548c CSRF \u653b\u51fb\u4ee5\u53ca\u5982\u4f55\u9884\u9632\u5b83\u4eec\u3002\u8fd9\u4e24\u4e2a\u6f0f\u6d1e\u7ecf\u5e38\u51fa\u73b0\u5728 OWASP \u5341\u5927\u6700\u5173\u952e\u7684 Web \u5e94\u7528\u7a0b\u5e8f\u98ce\u9669\u5217\u8868\u4e2d\uff0c\u56e0\u6b64\u4e86\u89e3\u5b83\u4eec\u5e76\u907f\u514d\u5c06\u5b83\u4eec\u5f15\u5165\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u975e\u5e38\u91cd\u8981\u3002<\/p>\n<p><b>TIP<\/b>  OWASP publishes the list online, with descriptions of each attack and how to prevent those attacks. There\u2019s a cheat sheet for staying safe here: <a href=\"https:\/\/cheatsheetseries.owasp.org\">https:\/\/cheatsheetseries.owasp.org<\/a>.<br \/>\n\u63d0\u793a:OWASP \u5728\u7ebf\u53d1\u5e03\u8be5\u5217\u8868\uff0c\u5176\u4e2d\u5305\u542b\u6bcf\u79cd\u653b\u51fb\u7684\u63cf\u8ff0\u4ee5\u53ca\u5982\u4f55\u9632\u6b62\u8fd9\u4e9b\u653b\u51fb\u3002\u8fd9\u91cc\u6709\u4e00\u5f20\u4fdd\u6301\u5b89\u5168\u7684\u5907\u5fd8\u5355\uff1a<a href=\"https:\/\/cheatsheetseries.owasp.org\">https:\/\/cheatsheetseries.owasp.org<\/a>\u3002<\/p>\n<p>In this section I\u2019ll provide an overview of some of the other most common vulnerabilities and how to avoid them in your apps.<br \/>\n\u5728\u672c\u8282\u4e2d\uff0c\u6211\u5c06\u6982\u8ff0\u5176\u4ed6\u4e00\u4e9b\u6700\u5e38\u89c1\u7684\u6f0f\u6d1e\uff0c\u4ee5\u53ca\u5982\u4f55\u5728\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u907f\u514d\u5b83\u4eec\u3002<\/p>\n<h3>29.4.1 Detecting and avoiding open redirect attacks\u200c<\/h3>\n<p>29.4.1 \u68c0\u6d4b\u548c\u907f\u514d\u5f00\u653e\u91cd\u5b9a\u5411\u653b\u51fb<\/p>\n<p>A common OWASP vulnerability is due to open redirect attacks. An open redirect attack occurs when a user clicks a link to an otherwise-safe app and ends up being redirected to a malicious website, such as one that serves malware. The safe app contains no direct links to the malicious website, so how does this happen?<br \/>\n\u4e00\u4e2a\u5e38\u89c1\u7684 OWASP \u6f0f\u6d1e\u662f\u7531\u4e8e\u5f00\u653e\u91cd\u5b9a\u5411\u653b\u51fb\u9020\u6210\u7684\u3002\u5f53\u7528\u6237\u70b9\u51fb\u6307\u5411\u5176\u4ed6\u5b89\u5168\u5e94\u7528\u7a0b\u5e8f\u7684\u94fe\u63a5\u5e76\u6700\u7ec8\u88ab\u91cd\u5b9a\u5411\u5230\u6076\u610f\u7f51\u7ad9\uff08\u4f8b\u5982\u63d0\u4f9b\u6076\u610f\u8f6f\u4ef6\u7684\u7f51\u7ad9\uff09\u65f6\uff0c\u5c31\u4f1a\u53d1\u751f\u5f00\u653e\u91cd\u5b9a\u5411\u653b\u51fb\u3002\u5b89\u5168\u5e94\u7528\u7a0b\u5e8f\u4e0d\u5305\u542b\u6307\u5411\u6076\u610f\u7f51\u7ad9\u7684\u76f4\u63a5\u94fe\u63a5\uff0c\u90a3\u4e48\u8fd9\u662f\u600e\u4e48\u53d1\u751f\u7684\u5462\uff1f<\/p>\n<p>Open redirect attacks occur where the next page is passed as a parameter to an endpoint. The most common example is when you\u2019re logging in to an app. Typically, apps remember the page a user is on before redirecting them to a login page by passing the current page as a returnUrl query string parameter. After the user logs in, the app redirects the user to the returnUrl to carry on where they left o\ufb00.<br \/>\n\u5f53\u4e0b\u4e00\u9875\u4f5c\u4e3a\u53c2\u6570\u4f20\u9012\u7ed9\u7ec8\u7aef\u8282\u70b9\u65f6\uff0c\u4f1a\u53d1\u751f\u5f00\u653e\u91cd\u5b9a\u5411\u653b\u51fb\u3002\u6700\u5e38\u89c1\u7684\u793a\u4f8b\u662f\u5f53\u60a8\u767b\u5f55\u5e94\u7528\u7a0b\u5e8f\u65f6\u3002\u901a\u5e38\uff0c\u5e94\u7528\u7a0b\u5e8f\u4f1a\u8bb0\u4f4f\u7528\u6237\u6240\u5728\u7684\u9875\u9762\uff0c\u7136\u540e\u901a\u8fc7\u5c06\u5f53\u524d\u9875\u9762\u4f5c\u4e3a returnUrl \u67e5\u8be2\u5b57\u7b26\u4e32\u53c2\u6570\u4f20\u9012\uff0c\u5c06\u7528\u6237\u91cd\u5b9a\u5411\u5230\u767b\u5f55\u9875\u9762\u3002\u7528\u6237\u767b\u5f55\u540e\uff0c\u5e94\u7528\u7a0b\u5e8f\u4f1a\u5c06\u7528\u6237\u91cd\u5b9a\u5411\u5230 returnUrl \u4ee5\u4ece\u4ed6\u4eec\u79bb\u5f00\u7684\u4f4d\u7f6e\u7ee7\u7eed\u3002<\/p>\n<p>Imagine a user is browsing an e-commerce site. They click Buy for a product and are redirected to the login page. The product page they were on is passed as the returnUrl, so after they log in, they\u2019re redirected to the product page instead of being dumped back to the home screen.<br \/>\n\u5047\u8bbe\u7528\u6237\u6b63\u5728\u6d4f\u89c8\u4e00\u4e2a\u7535\u5b50\u5546\u52a1\u7f51\u7ad9\u3002\u4ed6\u4eec\u5355\u51fb\u4ea7\u54c1\u7684 Buy \uff08\u8d2d\u4e70\uff09 \u5e76\u88ab\u91cd\u5b9a\u5411\u5230\u767b\u5f55\u9875\u9762\u3002\u4ed6\u4eec\u6240\u5728\u7684\u4ea7\u54c1\u9875\u9762\u4f5c\u4e3a returnUrl \u4f20\u9012\uff0c\u56e0\u6b64\u5728\u4ed6\u4eec\u767b\u5f55\u540e\uff0c\u4ed6\u4eec\u4f1a\u88ab\u91cd\u5b9a\u5411\u5230\u4ea7\u54c1\u9875\u9762\uff0c\u800c\u4e0d\u662f\u88ab\u8f6c\u50a8\u56de\u4e3b\u5c4f\u5e55\u3002<\/p>\n<p>An open redirect attack takes advantage of this common pattern, as shown in \ufb01gure 29.9. A malicious attacker creates a login URL where the returnUrl is set to the website they want to send the user to and convinces the user to click the link to your web app. After the user logs in, a vulnerable app redirects the user to the malicious site.<br \/>\n\u5f00\u653e\u91cd\u5b9a\u5411\u653b\u51fb\u5229\u7528\u4e86\u8fd9\u79cd\u5e38\u89c1\u6a21\u5f0f\uff0c\u5982\u56fe 29.9 \u6240\u793a\u3002\u6076\u610f\u653b\u51fb\u8005\u521b\u5efa\u4e00\u4e2a\u767b\u5f55 URL\uff0c\u5176\u4e2d returnUrl \u8bbe\u7f6e\u4e3a\u4ed6\u4eec\u8981\u5c06\u7528\u6237\u53d1\u9001\u5230\u7684\u7f51\u7ad9\uff0c\u5e76\u8bf4\u670d\u7528\u6237\u5355\u51fb\u6307\u5411\u60a8\u7684 Web \u5e94\u7528\u7a0b\u5e8f\u7684\u94fe\u63a5\u3002\u7528\u6237\u767b\u5f55\u540e\uff0c\u6613\u53d7\u653b\u51fb\u7684\u5e94\u7528\u7a0b\u5e8f\u4f1a\u5c06\u7528\u6237\u91cd\u5b9a\u5411\u5230\u6076\u610f\u7ad9\u70b9\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/2909.jpg\" alt=\"alt text\" \/><\/p>\n<p>Figure 29.9 An open redirect makes use of the common return URL pattern. This is typically used for login pages but may be used in other areas of your app too. If your app doesn\u2019t verify that the URL is safe before redirecting the user, it could redirect users to malicious sites.<br \/>\n\u56fe 29.9 \u5f00\u653e\u91cd\u5b9a\u5411\u4f7f\u7528\u5e38\u89c1\u7684\u8fd4\u56de URL \u6a21\u5f0f\u3002\u8fd9\u901a\u5e38\u7528\u4e8e\u767b\u5f55\u9875\u9762\uff0c\u4f46\u4e5f\u53ef\u80fd\u7528\u4e8e\u5e94\u7528\u7a0b\u5e8f\u7684\u5176\u4ed6\u533a\u57df\u3002\u5982\u679c\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u5728\u91cd\u5b9a\u5411\u7528\u6237\u4e4b\u524d\u672a\u9a8c\u8bc1 URL \u662f\u5426\u5b89\u5168\uff0c\u5219\u53ef\u80fd\u4f1a\u5c06\u7528\u6237\u91cd\u5b9a\u5411\u5230\u6076\u610f\u7f51\u7ad9\u3002<\/p>\n<p>The simple solution to this attack is to always validate that the returnUrl is a local URL that belongs to your app before redirecting users to it. The default Identity UI does this already, so you shouldn\u2019t have to worry about the login page if you\u2019re using Identity, as described in chapter 23.<br \/>\n\u8fd9\u79cd\u653b\u51fb\u7684\u7b80\u5355\u89e3\u51b3\u65b9\u6848\u662f\u5728\u5c06\u7528\u6237\u91cd\u5b9a\u5411\u5230 returnUrl \u4e4b\u524d\uff0c\u59cb\u7ec8\u9a8c\u8bc1 returnUrl \u662f\u5426\u662f\u5c5e\u4e8e\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u7684\u672c\u5730 URL\u3002\u9ed8\u8ba4\u7684 Identity UI \u5df2\u7ecf\u8fd9\u6837\u505a\u4e86\uff0c\u56e0\u6b64\u5982\u679c\u60a8\u4f7f\u7528\u7684\u662f Identity\uff0c\u5219\u4e0d\u5fc5\u62c5\u5fc3\u767b\u5f55\u9875\u9762\uff0c\u5982\u7b2c 23 \u7ae0\u6240\u8ff0\u3002<\/p>\n<p>If you have redirects in other parts of your app, ASP.NET Core provides a couple of helper methods for staying safe, the most useful of which is Url.IsLocalUrl(). Listing 29.5 shows how you could verify that a provided return URL is safe and, if not, redirect to the app\u2019s home page.<br \/>\n\u5982\u679c\u60a8\u5728\u5e94\u7528\u7a0b\u5e8f\u7684\u5176\u4ed6\u90e8\u5206\u6709\u91cd\u5b9a\u5411\uff0cASP.NET Core \u63d0\u4f9b\u4e86\u51e0\u4e2a\u5e2e\u52a9\u7a0b\u5e8f\u65b9\u6cd5\u6765\u4fdd\u6301\u5b89\u5168\uff0c\u5176\u4e2d\u6700\u6709\u7528\u7684\u662f Url.IsLocalUrl\uff08\uff09\u3002\u6e05\u5355 29.5 \u663e\u793a\u4e86\u5982\u4f55\u9a8c\u8bc1\u63d0\u4f9b\u7684\u8fd4\u56de URL \u662f\u5426\u5b89\u5168\uff0c\u5982\u679c\u4e0d\u662f\uff0c\u5219\u91cd\u5b9a\u5411\u5230\u5e94\u7528\u7a0b\u5e8f\u7684\u4e3b\u9875\u3002<\/p>\n<p>You can also use the LocalRedirect() helper method on the ControllerBase and Razor Page PageModel classes, which throw an exception if the provided URL isn\u2019t local.\u200c\u200c<br \/>\n\u8fd8\u53ef\u4ee5\u5728 ControllerBase \u548c Razor Page PageModel \u7c7b\u4e0a\u4f7f\u7528 LocalRedirect\uff08\uff09 \u5e2e\u52a9\u7a0b\u5e8f\u65b9\u6cd5\uff0c\u5982\u679c\u63d0\u4f9b\u7684 URL \u4e0d\u662f\u672c\u5730\u7684\uff0c\u5219\u4f1a\u5f15\u53d1\u5f02\u5e38\u3002<\/p>\n<p>Listing 29.5 Detecting open redirect attacks by checking for local return URLs<br \/>\n\u6e05\u5355 29.5 \u901a\u8fc7\u68c0\u67e5\u672c\u5730\u8fd4\u56de URL \u6765\u68c0\u6d4b\u5f00\u653e\u91cd\u5b9a\u5411\u653b\u51fb<\/p>\n<pre><code>[HttpPost]\npublic async Task&lt;IActionResult&gt; Login(\nLoginViewModel model, string returnUrl = null) \u2776\n{\n\/\/ Verify password, and sign user in\nif (Url.IsLocalUrl(returnUrl)) \u2777\n{\nreturn Redirect(returnUrl); \u2778\n}\nelse\n{\nreturn RedirectToPage(&quot;Index&quot;); \u2779\n}\n}<\/code><\/pre>\n<p>\u2776 The return URL is provided as an argument to the action method.<br \/>\n\u8fd4\u56de URL \u4f5c\u4e3a\u4f5c\u65b9\u6cd5\u7684\u53c2\u6570\u63d0\u4f9b\u3002<\/p>\n<p>\u2777 Returns true if the return URL starts with \/ or ~\/<br \/>\n\u5982\u679c\u8fd4\u56de URL \u4ee5 \/ \u6216 ~\/\u5f00\u5934\uff0c\u5219\u8fd4\u56de true<\/p>\n<p>\u2778 The URL is local, so it\u2019s safe to redirect to it.<br \/>\n\u8be5 URL \u662f\u672c\u5730\u7684\uff0c\u56e0\u6b64\u53ef\u4ee5\u5b89\u5168\u5730\u91cd\u5b9a\u5411\u5230\u5b83\u3002<\/p>\n<p>\u2779 The URL was not local and could be an open redirect attack, so redirect to the homepage for safety.<br \/>\n\u8be5 URL \u4e0d\u662f\u672c\u5730\u7684\uff0c\u53ef\u80fd\u662f\u516c\u5f00\u91cd\u5b9a\u5411\u653b\u51fb\uff0c\u56e0\u6b64\u4e3a\u4e86\u5b89\u5168\u8d77\u89c1\uff0c\u8bf7\u91cd\u5b9a\u5411\u5230\u4e3b\u9875\u3002<\/p>\n<p>This simple pattern protects against open redirect attacks that could otherwise expose your users to malicious content. Whenever you\u2019re redirecting to a URL that comes from a query string or other user input, you should use this pattern.<br \/>\n\u8fd9\u79cd\u7b80\u5355\u7684\u6a21\u5f0f\u53ef\u4ee5\u9632\u6b62\u5f00\u653e\u91cd\u5b9a\u5411\u653b\u51fb\uff0c\u5426\u5219\u53ef\u80fd\u4f1a\u4f7f\u60a8\u7684\u7528\u6237\u63a5\u89e6\u5230\u6076\u610f\u5185\u5bb9\u3002\u6bcf\u5f53\u91cd\u5b9a\u5411\u5230\u6765\u81ea\u67e5\u8be2\u5b57\u7b26\u4e32\u6216\u5176\u4ed6\u7528\u6237\u8f93\u5165\u7684 URL \u65f6\uff0c\u90fd\u5e94\u4f7f\u7528\u6b64\u6a21\u5f0f\u3002<\/p>\n<p><b>TIP<\/b>  In some authentication \ufb02ows, such as when authenticating with OpenID Connect, you can\u2019t redirect to a local URL, so you can\u2019t use this pattern. Instead, OpenID Connect requires that you preregister the allowed redirect URLs and redirect only to a registered URL. You should consider using this pattern when you can\u2019t enforce a local- only redirect.<br \/>\n\u63d0\u793a:\u5728\u67d0\u4e9b\u8eab\u4efd\u9a8c\u8bc1\u6d41\u4e2d\uff0c\u4f8b\u5982\u4f7f\u7528 OpenID Connect \u8fdb\u884c\u8eab\u4efd\u9a8c\u8bc1\u65f6\uff0c\u60a8\u65e0\u6cd5\u91cd\u5b9a\u5411\u5230\u672c\u5730 URL\uff0c\u56e0\u6b64\u4e0d\u80fd\u4f7f\u7528\u6b64\u6a21\u5f0f\u3002\u76f8\u53cd\uff0cOpenID Connect \u8981\u6c42\u60a8\u9884\u5148\u6ce8\u518c\u5141\u8bb8\u7684\u91cd\u5b9a\u5411 URL\uff0c\u5e76\u4e14\u4ec5\u91cd\u5b9a\u5411\u5230\u5df2\u6ce8\u518c\u7684 URL\u3002\u5f53\u60a8\u65e0\u6cd5\u5f3a\u5236\u6267\u884c\u4ec5\u9650\u672c\u5730\u7684\u91cd\u5b9a\u5411\u65f6\uff0c\u60a8\u5e94\u8be5\u8003\u8651\u4f7f\u7528\u6b64\u6a21\u5f0f\u3002<\/p>\n<p>Open redirect attacks present a risk to your users rather than to your app directly. The next vulnerability represents a critical vulnerability in your app itself.<br \/>\n\u5f00\u653e\u91cd\u5b9a\u5411\u653b\u51fb\u4f1a\u7ed9\u60a8\u7684\u7528\u6237\u5e26\u6765\u98ce\u9669\uff0c\u800c\u4e0d\u662f\u76f4\u63a5\u7ed9\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u5e26\u6765\u98ce\u9669\u3002\u4e0b\u4e00\u4e2a\u6f0f\u6d1e\u8868\u793a\u5e94\u7528\u7a0b\u5e8f\u672c\u8eab\u7684\u4e25\u91cd\u6f0f\u6d1e\u3002<\/p>\n<h3>29.4.2 Avoiding SQL injection attacks with EF Core and parameterization\u200c<\/h3>\n<p>29.4.2 \u4f7f\u7528 EF Core \u548c\u53c2\u6570\u5316\u907f\u514d SQL \u6ce8\u5165\u653b\u51fb<\/p>\n<p>SQL injection attacks represent one of the most dangerous threats to your application. Attackers craft simple malicious input, which they send to your application as traditional form-based input or by customizing URLs and query strings to execute arbitrary code against your database. An SQL injection vulnerability could expose your entire database to attackers, so it\u2019s critical that you spot and remove any such vulnerabilities in your apps.<br \/>\nSQL \u6ce8\u5165\u653b\u51fb\u662f\u5e94\u7528\u7a0b\u5e8f\u9762\u4e34\u7684\u6700\u5371\u9669\u7684\u5a01\u80c1\u4e4b\u4e00\u3002\u653b\u51fb\u8005\u5236\u4f5c\u7b80\u5355\u7684\u6076\u610f\u8f93\u5165\uff0c\u8fd9\u4e9b\u8f93\u5165\u4f5c\u4e3a\u4f20\u7edf\u7684\u57fa\u4e8e\u8868\u5355\u7684\u8f93\u5165\u53d1\u9001\u5230\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\uff0c\u6216\u8005\u901a\u8fc7\u81ea\u5b9a\u4e49 URL \u548c\u67e5\u8be2\u5b57\u7b26\u4e32\u6765\u9488\u5bf9\u60a8\u7684\u6570\u636e\u5e93\u6267\u884c\u4efb\u610f\u4ee3\u7801\u3002SQL \u6ce8\u5165\u6f0f\u6d1e\u53ef\u80fd\u4f1a\u5c06\u60a8\u7684\u6574\u4e2a\u6570\u636e\u5e93\u66b4\u9732\u7ed9\u653b\u51fb\u8005\uff0c\u56e0\u6b64\u53d1\u73b0\u5e76\u5220\u9664\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u4efb\u4f55\u6b64\u7c7b\u6f0f\u6d1e\u81f3\u5173\u91cd\u8981\u3002<\/p>\n<p>I hope I\u2019ve scared you a little with that introduction, so now for the good news: if you\u2019re using Entity Framework Core (EF Core) or pretty much any other object-relational mapper (ORM) in a standard way, you should be safe. EF Core has built-in protections against SQL injection, so as long as you\u2019re not doing anything funky, you should be \ufb01ne.<br \/>\n\u6211\u5e0c\u671b\u6211\u7684\u4ecb\u7ecd\u8ba9\u60a8\u6709\u70b9\u5bb3\u6015\uff0c\u6240\u4ee5\u73b0\u5728\u597d\u6d88\u606f\u662f\uff1a\u5982\u679c\u60a8\u4ee5\u6807\u51c6\u65b9\u5f0f\u4f7f\u7528 Entity Framework Core \uff08EF Core\uff09 \u6216\u51e0\u4e4e\u4efb\u4f55\u5176\u4ed6\u5bf9\u8c61\u5173\u7cfb\u6620\u5c04\u5668 \uff08ORM\uff09\uff0c\u60a8\u5e94\u8be5\u662f\u5b89\u5168\u7684\u3002EF Core \u5177\u6709\u9488\u5bf9 SQL \u6ce8\u5165\u7684\u5185\u7f6e\u4fdd\u62a4\u529f\u80fd\uff0c\u56e0\u6b64\u53ea\u8981\u60a8\u4e0d\u505a\u4efb\u4f55\u65f6\u9ae6\u7684\u4e8b\u60c5\uff0c\u5e94\u8be5\u6ca1\u95ee\u9898\u3002<\/p>\n<p>SQL injection vulnerabilities occur when you build SQL statements yourself and include dynamic input that an attacker provides, even indirectly. EF Core provides the ability to create raw SQL queries using the FromSqlRaw() method, so you must be careful when using this method.<br \/>\n\u5f53\u60a8\u81ea\u5df1\u6784\u5efa SQL \u8bed\u53e5\u5e76\u5305\u542b\u653b\u51fb\u8005\u63d0\u4f9b\u7684\u52a8\u6001\u8f93\u5165\uff08\u751a\u81f3\u662f\u95f4\u63a5\u63d0\u4f9b\u7684\uff09\u65f6\uff0c\u5c31\u4f1a\u51fa\u73b0 SQL \u6ce8\u5165\u6f0f\u6d1e\u3002EF Core \u63d0\u4f9b\u4e86\u4f7f\u7528 FromSqlRaw\uff08\uff09 \u65b9\u6cd5\u521b\u5efa\u539f\u59cb SQL \u67e5\u8be2\u7684\u529f\u80fd\uff0c\u56e0\u6b64\u5728\u4f7f\u7528\u6b64\u65b9\u6cd5\u65f6\u5fc5\u987b\u5c0f\u5fc3\u3002<\/p>\n<p>Imagine your recipe app has a search form that lets you search for a recipe by name. If you write the query using LINQ extension methods (as discussed in chapter 12), you would have no risk of SQL injection attacks. However, if you decide to write your SQL query by hand, you open yourself to such a vulnerability, as shown in the following listing.<br \/>\n\u5047\u8bbe\u60a8\u7684\u98df\u8c31\u5e94\u7528\u7a0b\u5e8f\u6709\u4e00\u4e2a\u641c\u7d22\u8868\u5355\uff0c\u53ef\u8ba9\u60a8\u6309\u540d\u79f0\u641c\u7d22\u98df\u8c31\u3002\u5982\u679c\u4f7f\u7528 LINQ \u6269\u5c55\u65b9\u6cd5\u7f16\u5199\u67e5\u8be2\uff08\u5982\u7b2c 12 \u7ae0\u6240\u8ff0\uff09\uff0c\u5219\u4e0d\u4f1a\u6709 SQL \u6ce8\u5165\u653b\u51fb\u7684\u98ce\u9669\u3002\u4f46\u662f\uff0c\u5982\u679c\u60a8\u51b3\u5b9a\u624b\u52a8\u7f16\u5199 SQL \u67e5\u8be2\uff0c\u5219\u53ef\u80fd\u4f1a\u9762\u4e34\u6b64\u7c7b\u6f0f\u6d1e\uff0c\u5982\u4e0b\u9762\u7684\u6e05\u5355\u6240\u793a\u3002<\/p>\n<p>Listing 29.6 An SQL injection vulnerability in EF Core due to string concatenation<br \/>\n\u5217\u8868 29.6 \u7531\u4e8e\u5b57\u7b26\u4e32\u4e32\u8054\u800c\u5bfc\u81f4\u7684 EF Core \u4e2d\u7684 SQL \u6ce8\u5165\u6f0f\u6d1e<\/p>\n<pre><code>public IList&lt;User&gt; FindRecipe(string search) \u2776\n{\nreturn _context.Recipes \u2777\n.FromSqlRaw(&quot;SELECT * FROM Recipes&quot; + \u2778\n&quot;WHERE Name = &#039;&quot; + search + &quot;&#039;&quot;) \u2779\n.ToList();\n}<\/code><\/pre>\n<p>\u2776 The search parameter comes from user input, so it\u2019s unsafe.<br \/>\nsearch \u53c2\u6570\u6765\u81ea\u7528\u6237\u8f93\u5165\uff0c\u56e0\u6b64\u4e0d\u5b89\u5168\u3002<\/p>\n<p>\u2777 The current EF Core DbContext is held in the _context field.<br \/>\n\u5f53\u524d EF Core DbContext \u4fdd\u5b58\u5728 _context \u5b57\u6bb5\u4e2d\u3002<\/p>\n<p>\u2778 You can write queries by hand using the FromSqlRaw extension method.<br \/>\n\u60a8\u53ef\u4ee5\u4f7f\u7528 FromSqlRaw \u6269\u5c55\u65b9\u6cd5\u624b\u52a8\u7f16\u5199\u67e5\u8be2\u3002<\/p>\n<p>\u2779 This introduces the vulnerability\u2014including unsafe content directly in an SQL string.<br \/>\n\u8fd9\u4f1a\u5f15\u5165\u6f0f\u6d1e \u2014 \u76f4\u63a5\u5728 SQL\u5b57\u7b26\u4e32\u4e2d\u5305\u542b\u4e0d\u5b89\u5168\u7684\u5185\u5bb9\u3002<\/p>\n<p>In this listing, the user input held in search is included directly in the SQL query. By crafting malicious input, users can potentially perform any operation on your database.<br \/>\n\u5728\u6b64\u6e05\u5355\u4e2d\uff0c\u641c\u7d22\u4e2d\u4fdd\u5b58\u7684\u7528\u6237\u8f93\u5165\u76f4\u63a5\u5305\u542b\u5728 SQL \u67e5\u8be2\u4e2d\u3002\u901a\u8fc7\u7cbe\u5fc3\u8bbe\u8ba1\u6076\u610f\u8f93\u5165\uff0c\u7528\u6237\u53ef\u80fd\u4f1a\u5bf9\u60a8\u7684\u6570\u636e\u5e93\u6267\u884c\u4efb\u4f55\u4f5c\u3002<\/p>\n<p>Imagine an attacker searches your website using the text<br \/>\n\u60f3\u8c61\u4e00\u4e0b\uff0c\u653b\u51fb\u8005\u4f7f\u7528\u6587\u672c<\/p>\n<pre><code>&#039;; DROP TABLE Recipes; --<\/code><\/pre>\n<p>Your app assigns this to the search parameter, and the SQL query executed against your database becomes<br \/>\n\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u5c06\u6b64\u53c2\u6570\u5206\u914d\u7ed9 search \u53c2\u6570\uff0c\u5e76\u4e14\u9488\u5bf9\u60a8\u7684\u6570\u636e\u5e93\u6267\u884c\u7684 SQL \u67e5\u8be2\u5c06\u53d8\u4e3a<\/p>\n<pre><code>SELECT * FROM Recipes WHERE Name = &#039;&#039;; DROP TABLE Recipes; --&#039;<\/code><\/pre>\n<p>Simply by entering text into the search form of your app, the attacker has deleted the entire Recipes table from your app! That\u2019s catastrophic, but an SQL injection vulnerability provides more or less unfettered access to your database.Even if you\u2019ve set up database permissions correctly to prevent this sort of destructive action, attackers will likely be able to read all the data from your database, including your users\u2019 details.<br \/>\n\u53ea\u9700\u5728\u5e94\u7528\u7684\u641c\u7d22\u8868\u5355\u4e2d\u8f93\u5165\u6587\u672c\uff0c\u653b\u51fb\u8005\u5c31\u4ece\u60a8\u7684\u5e94\u7528\u4e2d\u5220\u9664\u4e86\u6574\u4e2a Recipes \u8868\uff01\u8fd9\u662f\u707e\u96be\u6027\u7684\uff0c\u4f46 SQL \u6ce8\u5165\u6f0f\u6d1e\u6216\u591a\u6216\u5c11\u63d0\u4f9b\u4e86\u5bf9\u6570\u636e\u5e93\u7684\u4e0d\u53d7\u9650\u5236\u7684\u8bbf\u95ee\u3002\u5373\u4f7f\u60a8\u5df2\u6b63\u786e\u8bbe\u7f6e\u6570\u636e\u5e93\u6743\u9650\u4ee5\u9632\u6b62\u6b64\u7c7b\u7834\u574f\u6027\u4f5c\uff0c\u653b\u51fb\u8005\u4e5f\u53ef\u80fd\u80fd\u591f\u4ece\u60a8\u7684\u6570\u636e\u5e93\u8bfb\u53d6\u6240\u6709\u6570\u636e\uff0c\u5305\u62ec\u60a8\u7684\u7528\u6237\u7684\u8be6\u7ec6\u4fe1\u606f\u3002<\/p>\n<p>The simple way to prevent this from happening is to avoid creating SQL queries by hand this way. If you do need to write your own SQL queries, don\u2019t use string concatenation, as in listing 29.6. Instead, use parameterized queries, in which the (potentially unsafe) input data is separate from the query itself, as shown here.<br \/>\n\u9632\u6b62\u8fd9\u79cd\u60c5\u51b5\u53d1\u751f\u7684\u7b80\u5355\u65b9\u6cd5\u662f\u907f\u514d\u4ee5\u8fd9\u79cd\u65b9\u5f0f\u624b\u52a8\u521b\u5efa SQL \u67e5\u8be2\u3002\u5982\u679c\u4f60\u786e\u5b9e\u9700\u8981\u7f16\u5199\u81ea\u5df1\u7684 SQL \u67e5\u8be2\uff0c\u8bf7\u4e0d\u8981\u4f7f\u7528\u5b57\u7b26\u4e32\u8fde\u63a5\uff0c\u5982\u6e05\u5355 29.6 \u6240\u793a\u3002\u76f8\u53cd\uff0c\u8bf7\u4f7f\u7528\u53c2\u6570\u5316\u67e5\u8be2\uff0c\u5176\u4e2d\uff08\u53ef\u80fd\u4e0d\u5b89\u5168\u7684\uff09\u8f93\u5165\u6570\u636e\u4e0e\u67e5\u8be2\u672c\u8eab\u662f\u5206\u5f00\u7684\uff0c\u5982\u4e0b\u6240\u793a\u3002<\/p>\n<p>Listing 29.7 Avoiding SQL injection by using parameterization<br \/>\n\u793a\u4f8b 29.7 \u4f7f\u7528\u53c2\u6570\u5316\u907f\u514d SQL \u6ce8\u5165<\/p>\n<pre><code>public IList&lt;User&gt; FindRecipe(string search)\n{\nreturn _context.Recipes\n.FromSqlRaw( &quot;SELECT * FROM Recipes WHERE Name = &#039;{0}&#039;&quot;, \u2776\nsearch) \u2777\n.ToList();\n}<\/code><\/pre>\n<p>\u2776 The SQL query uses a placeholder {0} for the parameter.<br \/>\nSQL \u67e5\u8be2\u4f7f\u7528\u53c2\u6570\u7684\u5360\u4f4d\u7b26{0}\u3002<\/p>\n<p>\u2777 The dangerous input is passed as a parameter, separate from the query.<br \/>\n\u5371\u9669\u8f93\u5165\u4f5c\u4e3a\u53c2\u6570\u4f20\u9012\uff0c\u4e0e\u67e5\u8be2\u5206\u5f00\u3002<\/p>\n<p>Parameterized queries are not vulnerable to SQL injection attacks, so the attack presented earlier won\u2019t work. If you use EF Core or other ORMs to access data using standard LINQ queries, you won\u2019t be vulnerable to injection attacks. EF Core automatically creates all SQL queries using parameterized queries to protect you. Even if you\u2019re using the low-level ADO.NET database APIs, stick to parameterized queries!<br \/>\n\u53c2\u6570\u5316\u67e5\u8be2\u4e0d\u6613\u53d7\u5230 SQL \u6ce8\u5165\u653b\u51fb\uff0c\u56e0\u6b64\u524d\u9762\u4ecb\u7ecd\u7684\u653b\u51fb\u4e0d\u8d77\u4f5c\u7528\u3002\u5982\u679c\u4f7f\u7528 EF Core \u6216\u5176\u4ed6 ORM \u901a\u8fc7\u6807\u51c6 LINQ \u67e5\u8be2\u8bbf\u95ee\u6570\u636e\uff0c\u5219\u4e0d\u4f1a\u5bb9\u6613\u53d7\u5230\u6ce8\u5165\u653b\u51fb\u3002EF Core \u4f7f\u7528\u53c2\u6570\u5316\u67e5\u8be2\u81ea\u52a8\u521b\u5efa\u6240\u6709 SQL \u67e5\u8be2\u4ee5\u4fdd\u62a4\u4f60\u3002\u5373\u4f7f\u60a8\u4f7f\u7528\u7684\u662f\u4f4e\u7ea7 ADO.NET \u6570\u636e\u5e93 API\uff0c\u4e5f\u8bf7\u575a\u6301\u4f7f\u7528\u53c2\u6570\u5316\u67e5\u8be2\uff01<\/p>\n<p><b>NOTE<\/b>  I\u2019ve talked about SQL injection attacks only in terms of a relational database, but this vulnerability can appear in NoSQL and document databases too. Always use parameterized queries or the equivalent, and don\u2019t craft queries by concatenating strings with user input.<br \/>\n\u6ce8\u610f:\u6211\u4ec5\u4ece\u5173\u7cfb\u6570\u636e\u5e93\u7684\u89d2\u5ea6\u8ba8\u8bba\u4e86 SQL \u6ce8\u5165\u653b\u51fb\uff0c\u4f46\u6b64\u6f0f\u6d1e\u4e5f\u53ef\u80fd\u51fa\u73b0\u5728 NoSQL \u548c\u6587\u6863\u6570\u636e\u5e93\u4e2d\u3002\u59cb\u7ec8\u4f7f\u7528\u53c2\u6570\u5316\u67e5\u8be2\u6216\u7b49\u6548\u67e5\u8be2\uff0c\u5e76\u4e14\u4e0d\u8981\u901a\u8fc7\u5c06\u5b57\u7b26\u4e32\u4e0e\u7528\u6237\u8f93\u5165\u8fde\u63a5\u8d77\u6765\u6765\u521b\u5efa\u67e5\u8be2\u3002<\/p>\n<p>Injection attacks have been the number-one vulnerability on the web for more than a decade, so it\u2019s crucial to be aware of them and how they arise. Whenever you need to write raw SQL queries, make sure that you always use parameterized queries.<br \/>\n\u5341\u591a\u5e74\u6765\uff0c\u6ce8\u5165\u653b\u51fb\u4e00\u76f4\u662f Web \u4e0a\u7684\u5934\u53f7\u6f0f\u6d1e\uff0c\u56e0\u6b64\u4e86\u89e3\u5b83\u4eec\u53ca\u5176\u51fa\u73b0\u65b9\u5f0f\u81f3\u5173\u91cd\u8981\u3002\u6bcf\u5f53\u9700\u8981\u7f16\u5199\u539f\u59cb SQL \u67e5\u8be2\u65f6\uff0c\u8bf7\u786e\u4fdd\u59cb\u7ec8\u4f7f\u7528\u53c2\u6570\u5316\u67e5\u8be2\u3002<\/p>\n<p>The next vulnerability is also related to attackers accessing data they shouldn\u2019t be able to. It\u2019s a little subtler than a direct injection attack but is trivial to perform; the only skill the attacker needs is the ability to count.<br \/>\n\u4e0b\u4e00\u4e2a\u6f0f\u6d1e\u8fd8\u4e0e\u653b\u51fb\u8005\u8bbf\u95ee\u4ed6\u4eec\u4e0d\u5e94\u8be5\u8bbf\u95ee\u7684\u6570\u636e\u6709\u5173\u3002\u5b83\u6bd4\u76f4\u63a5\u6ce8\u5165\u653b\u51fb\u66f4\u5fae\u5999\u4e00\u4e9b\uff0c\u4f46\u6267\u884c\u8d77\u6765\u5f88\u7b80\u5355;\u653b\u51fb\u8005\u552f\u4e00\u9700\u8981\u7684\u6280\u80fd\u662f\u8ba1\u6570\u80fd\u529b\u3002<\/p>\n<h3>29.4.3 Preventing insecure direct object references\u200c<\/h3>\n<p>29.4.3 \u9632\u6b62\u4e0d\u5b89\u5168\u7684\u76f4\u63a5\u5bf9\u8c61\u5f15\u7528<\/p>\n<p>Insecure direct object reference is a bit of a mouthful, but it means users accessing things they shouldn\u2019t by noticing patterns in URLs. Let\u2019s revisit our old friend the recipe app. As a reminder, the app shows you a list of recipes. You can view any of them, but you can edit only recipes you created yourself. When you view someone else\u2019s recipe, there\u2019s no Edit button visible.\u200c<br \/>\n\u4e0d\u5b89\u5168\u7684\u76f4\u63a5\u5bf9\u8c61\u5f15\u7528\u6709\u70b9\u62d7\u53e3\uff0c\u4f46\u8fd9\u610f\u5473\u7740\u7528\u6237\u901a\u8fc7\u6ce8\u610f\u5230 URL \u4e2d\u7684\u6a21\u5f0f\u6765\u8bbf\u95ee\u4ed6\u4eec\u4e0d\u5e94\u8be5\u8bbf\u95ee\u7684\u5185\u5bb9\u3002\u8ba9\u6211\u4eec\u91cd\u6e29\u4e00\u4e0b\u6211\u4eec\u7684\u8001\u670b\u53cb\u98df\u8c31\u5e94\u7528\u7a0b\u5e8f\u3002\u63d0\u9192\u4e00\u4e0b\uff0c\u8be5\u5e94\u7528\u7a0b\u5e8f\u4f1a\u5411\u60a8\u663e\u793a\u98df\u8c31\u5217\u8868\u3002\u60a8\u53ef\u4ee5\u67e5\u770b\u5176\u4e2d\u4efb\u4f55\u4e00\u4e2a\uff0c\u4f46\u53ea\u80fd\u7f16\u8f91\u60a8\u81ea\u5df1\u521b\u5efa\u7684\u914d\u65b9\u3002\u5f53\u60a8\u67e5\u770b\u5176\u4ed6\u4eba\u7684\u914d\u65b9\u65f6\uff0c\u6ca1\u6709\u53ef\u89c1\u7684 Edit \uff08\u7f16\u8f91\uff09 \u6309\u94ae\u3002<\/p>\n<p>A user clicks the Edit button on one of their recipes and notices that the URL is \/Recipes\/Edit\/120. That 120 is a dead giveaway as being the underlying database ID of the entity you\u2019re editing. A simple attack would be to change that ID to gain access to a di\ufb00erent entity, one that you wouldn\u2019t normally have access to. The user could try entering \/Recipes\/Edit\/121. If that lets them edit or view a recipe that they shouldn\u2019t be able to, you have an insecure direct object reference vulnerability.<br \/>\n\u7528\u6237\u5355\u51fb\u5176\u4e2d\u4e00\u4e2a\u914d\u65b9\u4e0a\u7684 Edit \uff08\u7f16\u8f91\uff09 \u6309\u94ae\uff0c\u5e76\u6ce8\u610f\u5230 URL \u4e3a \/Recipes\/Edit\/120\u3002\u8fd9 120 \u662f\u4e00\u4e2a\u6b7b\u7684\u8d60\u54c1\uff0c\u56e0\u4e3a\u8fd9\u662f\u60a8\u6b63\u5728\u7f16\u8f91\u7684\u5b9e\u4f53\u7684\u57fa\u7840\u6570\u636e\u5e93 ID\u3002\u4e00\u4e2a\u7b80\u5355\u7684\u653b\u51fb\u662f\u66f4\u6539\u8be5 ID \u4ee5\u83b7\u5f97\u5bf9\u4e0d\u540c\u5b9e\u4f53\u7684\u8bbf\u95ee\u6743\u9650\uff0c\u8be5\u5b9e\u4f53\u901a\u5e38\u65e0\u6743\u8bbf\u95ee\u3002\u7528\u6237\u53ef\u4ee5\u5c1d\u8bd5\u8f93\u5165 \/Recipes\/Edit\/121\u3002\u5982\u679c\u8fd9\u5141\u8bb8\u4ed6\u4eec\u7f16\u8f91\u6216\u67e5\u770b\u4ed6\u4eec\u4e0d\u5e94\u8be5\u80fd\u591f\u7f16\u8f91\u6216\u67e5\u770b\u7684\u914d\u65b9\uff0c\u5219\u60a8\u5b58\u5728\u4e0d\u5b89\u5168\u7684\u76f4\u63a5\u5bf9\u8c61\u5f15\u7528\u6f0f\u6d1e\u3002<\/p>\n<p>The solution to this problem is simple: you should have resource-based authorization in your endpoint handlers. If a user attempts to access an entity they\u2019re not allowed to access, they should get a permission-denied error. They shouldn\u2019t be able to bypass your authorization by typing a URL directly into the search bar of their browser.<br \/>\n\u6b64\u95ee\u9898\u7684\u89e3\u51b3\u65b9\u6848\u5f88\u7b80\u5355\uff1a\u60a8\u5e94\u8be5\u5728\u7ec8\u7aef\u8282\u70b9\u5904\u7406\u7a0b\u5e8f\u4e2d\u5177\u6709\u57fa\u4e8e\u8d44\u6e90\u7684\u6388\u6743\u3002\u5982\u679c\u7528\u6237\u5c1d\u8bd5\u8bbf\u95ee\u4e0d\u5141\u8bb8\u4ed6\u4eec\u8bbf\u95ee\u7684\u5b9e\u4f53\uff0c\u4ed6\u4eec\u5e94\u8be5\u4f1a\u6536\u5230 permission-denied \u9519\u8bef\u3002\u4ed6\u4eec\u4e0d\u5e94\u8be5\u80fd\u591f\u901a\u8fc7\u5728\u6d4f\u89c8\u5668\u7684\u641c\u7d22\u680f\u4e2d\u76f4\u63a5\u8f93\u5165 URL \u6765\u7ed5\u8fc7\u60a8\u7684\u6388\u6743\u3002<\/p>\n<p>In ASP.NET Core apps, this vulnerability typically arises when you attempt to restrict users by hiding elements from your UI, such as by hiding the Edit button. Instead, you should use resource-based authorization, as discussed in chapter 24.<br \/>\n\u5728 ASP.NET Core \u5e94\u7528\u7a0b\u5e8f\u4e2d\uff0c\u5f53\u60a8\u5c1d\u8bd5\u901a\u8fc7\u9690\u85cf UI \u4e2d\u7684\u5143\u7d20\uff08\u4f8b\u5982\u9690\u85cf Edit \u6309\u94ae\uff09\u6765\u9650\u5236\u7528\u6237\u65f6\uff0c\u901a\u5e38\u4f1a\u51fa\u73b0\u6b64\u6f0f\u6d1e\u3002\u76f8\u53cd\uff0c\u60a8\u5e94\u8be5\u4f7f\u7528\u57fa\u4e8e\u8d44\u6e90\u7684\u6388\u6743\uff0c\u5982 Chapter 24 \u4e2d\u6240\u8ff0\u3002<\/p>\n<p><b>WARNING<\/b> You must always use resource-based authorization to restrict which entities a user can access. Hiding or disabling UI elements provides an improved user experience, but it isn\u2019t a security measure.<br \/>\n\u8b66\u544a\u60a8\u5fc5\u987b\u59cb\u7ec8\u4f7f\u7528\u57fa\u4e8e\u8d44\u6e90\u7684\u6388\u6743\u6765\u9650\u5236\u7528\u6237\u53ef\u4ee5\u8bbf\u95ee\u7684\u5b9e\u4f53\u3002\u9690\u85cf\u6216\u7981\u7528 UI \u5143\u7d20\u53ef\u4ee5\u63d0\u4f9b\u66f4\u597d\u7684\u7528\u6237\u4f53\u9a8c\uff0c\u4f46\u8fd9\u4e0d\u662f\u4e00\u9879\u5b89\u5168\u63aa\u65bd\u3002<\/p>\n<p>You can sidestep this vulnerability somewhat by avoiding integer IDs for your entities in the URLs, perhaps by using a pseudorandom globally unique identi\ufb01er (GUID) such as C2E296BA-7EA8-4195-9CA7-C323304CCD12 instead.<br \/>\n\u60a8\u53ef\u4ee5\u901a\u8fc7\u907f\u514d\u5728 URL \u4e2d\u4f7f\u7528\u5b9e\u4f53\u7684\u6574\u6570 ID \u6765\u7a0d\u5fae\u56de\u907f\u6b64\u6f0f\u6d1e\uff0c\u4e5f\u8bb8\u53ef\u4ee5\u6539\u7528\u4f2a\u968f\u673a\u5168\u5c40\u552f\u4e00\u6807\u8bc6\u7b26 \uff08GUID\uff09\uff0c\u4f8b\u5982 C2E296BA-7EA8-4195-9CA7-C323304CCD12\u3002<\/p>\n<p>This makes the process of guessing other entities harder, as you can\u2019t simply add 1 to an existing number, but it\u2019s masking the problem rather than \ufb01xing it. Nevertheless, using GUIDs can be useful when you want to have publicly accessible pages that don\u2019t require authentication but don\u2019t want their IDs to be easily discoverable.<br \/>\n\u8fd9\u4f7f\u5f97\u731c\u6d4b\u5176\u4ed6\u5b9e\u4f53\u7684\u8fc7\u7a0b\u66f4\u52a0\u56f0\u96be\uff0c\u56e0\u4e3a\u4f60\u4e0d\u80fd\u7b80\u5355\u5730\u5c06 1 \u6dfb\u52a0\u5230\u73b0\u6709\u6570\u5b57\u4e0a\uff0c\u4f46\u5b83\u63a9\u76d6\u4e86\u95ee\u9898\uff0c\u800c\u4e0d\u662f\u89e3\u51b3\u95ee\u9898\u3002\u4e0d\u8fc7\uff0c\u5f53\u60a8\u5e0c\u671b\u62e5\u6709\u4e0d\u9700\u8981\u8eab\u4efd\u9a8c\u8bc1\u4f46\u53c8\u4e0d\u5e0c\u671b\u5176 ID \u6613\u4e8e\u53d1\u73b0\u7684\u53ef\u516c\u5f00\u8bbf\u95ee\u9875\u9762\u65f6\uff0c\u4f7f\u7528 GUID \u53ef\u80fd\u5f88\u6709\u7528\u3002<\/p>\n<p>The \ufb01nal section in this chapter doesn\u2019t deal with a single vulnerability. Instead, I discuss a separate but related problem: protecting your users\u2019 data.<br \/>\n\u672c\u7ae0\u7684\u6700\u540e\u4e00\u8282\u4e0d\u6d89\u53ca\u5355\u4e2a\u6f0f\u6d1e\u3002\u76f8\u53cd\uff0c\u6211\u8ba8\u8bba\u4e86\u4e00\u4e2a\u5355\u72ec\u4f46\u76f8\u5173\u7684\u95ee\u9898\uff1a\u4fdd\u62a4\u7528\u6237\u7684\u6570\u636e\u3002<\/p>\n<h3>29.4.4 Protecting your users\u2019 passwords and data\u200c<\/h3>\n<p>29.4.4 \u4fdd\u62a4\u7528\u6237\u7684\u53e3\u4ee4\u548c\u6570\u636e<\/p>\n<p>For many apps, the most sensitive data you\u2019ll be storing is the personal data of your users. This could include emails, passwords, address details, or payment information. You should be careful when storing any of this data. As well as presenting an inviting target for attackers, you may have legal obligations for how you handle it, such as data protection laws and Payment Card Industry (PCI) compliance requirements.<br \/>\n\u5bf9\u4e8e\u8bb8\u591a\u5e94\u7528\u7a0b\u5e8f\uff0c\u60a8\u5c06\u5b58\u50a8\u7684\u6700\u654f\u611f\u6570\u636e\u662f\u7528\u6237\u7684\u4e2a\u4eba\u6570\u636e\u3002\u8fd9\u53ef\u80fd\u5305\u62ec\u7535\u5b50\u90ae\u4ef6\u3001\u5bc6\u7801\u3001\u5730\u5740\u8be6\u7ec6\u4fe1\u606f\u6216\u4ed8\u6b3e\u4fe1\u606f\u3002\u5728\u5b58\u50a8\u4efb\u4f55\u6b64\u7c7b\u6570\u636e\u65f6\uff0c\u60a8\u5e94\u8be5\u5c0f\u5fc3\u3002\u9664\u4e86\u4e3a\u653b\u51fb\u8005\u63d0\u4f9b\u8bf1\u4eba\u7684\u76ee\u6807\u5916\uff0c\u60a8\u53ef\u80fd\u8fd8\u5bf9\u5982\u4f55\u5904\u7406\u5b83\u8d1f\u6709\u6cd5\u5f8b\u4e49\u52a1\uff0c\u4f8b\u5982\u6570\u636e\u4fdd\u62a4\u6cd5\u548c\u652f\u4ed8\u5361\u884c\u4e1a \uff08PCI\uff09 \u5408\u89c4\u6027\u8981\u6c42\u3002<\/p>\n<p>The easiest way to protect yourself is to not store data you don\u2019t need. If you don\u2019t need your user\u2019s address, don\u2019t ask for it. That way, you can\u2019t lose it! Similarly, if you use a third- party identity service to store user details, as described in chapter 23, you won\u2019t have to work as hard to protect your users\u2019 personal information.<br \/>\n\u4fdd\u62a4\u81ea\u5df1\u7684\u6700\u7b80\u5355\u65b9\u6cd5\u662f\u4e0d\u5b58\u50a8\u60a8\u4e0d\u9700\u8981\u7684\u6570\u636e\u3002\u5982\u679c\u60a8\u4e0d\u9700\u8981\u7528\u6237\u7684\u5730\u5740\uff0c\u8bf7\u4e0d\u8981\u8be2\u95ee\u3002\u8fd9\u6837\uff0c\u4f60\u5c31\u4e0d\u4f1a\u4e22\u5931\u5b83\uff01\u540c\u6837\uff0c\u5982\u679c\u60a8\u4f7f\u7528\u7b2c\u4e09\u65b9\u8eab\u4efd\u670d\u52a1\u6765\u5b58\u50a8\u7528\u6237\u8be6\u7ec6\u4fe1\u606f\uff0c\u5982\u7b2c 23 \u7ae0\u6240\u8ff0\uff0c\u5219\u4e0d\u5fc5\u8d39\u529b\u5730\u4fdd\u62a4\u7528\u6237\u7684\u4e2a\u4eba\u4fe1\u606f\u3002<\/p>\n<p>If you store user details in your own app or build your own identity provider, then you need to make sure to follow best practices when handling user information. The new project templates that use ASP.NET Core Identity follow most of these practices by default, so I highly recommend you start from one of these. You need to consider many aspects, too many to go into detail here,1 but they include the following:<br \/>\n\u5982\u679c\u60a8\u5c06\u7528\u6237\u8be6\u7ec6\u4fe1\u606f\u5b58\u50a8\u5728\u81ea\u5df1\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u6216\u6784\u5efa\u81ea\u5df1\u7684\u8eab\u4efd\u63d0\u4f9b\u5546\uff0c\u5219\u9700\u8981\u786e\u4fdd\u5728\u5904\u7406\u7528\u6237\u4fe1\u606f\u65f6\u9075\u5faa\u6700\u4f73\u5b9e\u8df5\u3002\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u4f7f\u7528 ASP.NET Core Identity \u7684\u65b0\u9879\u76ee\u6a21\u677f\u9075\u5faa\u5176\u4e2d\u7684\u5927\u90e8\u5206\u505a\u6cd5\uff0c\u56e0\u6b64\u6211\u5f3a\u70c8\u5efa\u8bae\u60a8\u4ece\u5176\u4e2d\u4e00\u79cd\u505a\u6cd5\u5f00\u59cb\u3002\u60a8\u9700\u8981\u8003\u8651\u8bb8\u591a\u65b9\u9762\uff0c\u592a\u591a\u4e86\uff0c\u65e0\u6cd5\u5728\u8fd9\u91cc\u8be6\u7ec6\u4ecb\u7ecd\uff0c1\u4f46\u5b83\u4eec\u5305\u62ec\u4ee5\u4e0b\u5185\u5bb9\uff1a<\/p>\n<p>\u2022  Never store user passwords anywhere directly. You should store only cryptographic hashes computed using an expensive hashing algorithm, such as BCrypt or PBKDF2.<br \/>\n\u5207\u52ff\u5c06\u7528\u6237\u5bc6\u7801\u76f4\u63a5\u5b58\u50a8\u5728\u4efb\u4f55\u4f4d\u7f6e\u3002\u60a8\u5e94\u8be5\u53ea\u5b58\u50a8\u4f7f\u7528\u6602\u8d35\u7684\u54c8\u5e0c\u7b97\u6cd5\uff08\u5982 BCrypt \u6216 PBKDF2\uff09\u8ba1\u7b97\u7684\u52a0\u5bc6\u54c8\u5e0c\u3002<\/p>\n<p>\u2022  Don\u2019t store more data than you need. You should never store credit card details.<br \/>\n\u4e0d\u8981\u5b58\u50a8\u8d85\u51fa\u60a8\u9700\u8981\u7684\u6570\u636e\u3002\u60a8\u6c38\u8fdc\u4e0d\u5e94\u8be5\u5b58\u50a8\u4fe1\u7528\u5361\u8be6\u7ec6\u4fe1\u606f\u3002<\/p>\n<p>\u2022  Allow users to use multifactor authentication (MFA) to sign in to your site.<br \/>\n\u5141\u8bb8\u7528\u6237\u4f7f\u7528\u591a\u91cd\u8eab\u4efd\u9a8c\u8bc1 \uff08MFA\uff09 \u767b\u5f55\u60a8\u7684\u7f51\u7ad9\u3002<\/p>\n<p>\u2022  Prevent users from using passwords that are known to be weak or compromised, such as disallowing dictionary words, sequential characters, and so on.<br \/>\n\u9632\u6b62\u7528\u6237\u4f7f\u7528\u5df2\u77e5\u8f83\u5f31\u6216\u5df2\u6cc4\u9732\u7684\u5bc6\u7801\uff0c\u4f8b\u5982\u4e0d\u5141\u8bb8\u4f7f\u7528\u5b57\u5178\u5355\u8bcd\u3001\u8fde\u7eed\u5b57\u7b26\u7b49\u3002<\/p>\n<p>\u2022  Mark authentication cookies as http (so that they can\u2019t be read using JavaScript) and secure so they\u2019ll be sent only over an HTTPS connection, never over HTTP. Where possible, you should also mark your cookies as SameSite=strict. See the documentation for details: <a href=\"http:\/\/mng.bz\/a11m\">http:\/\/mng.bz\/a11m<\/a>.<br \/>\n\u5c06\u8eab\u4efd\u9a8c\u8bc1 Cookie \u6807\u8bb0\u4e3a http\uff08\u8fd9\u6837\u5c31\u65e0\u6cd5\u4f7f\u7528 JavaScript \u8bfb\u53d6\u5b83\u4eec\uff09\u548c\u5b89\u5168\uff0c\u8fd9\u6837\u5b83\u4eec\u5c06\u4ec5\u901a\u8fc7 HTTPS \u8fde\u63a5\u53d1\u9001\uff0c\u800c\u4e0d\u662f\u901a\u8fc7 HTTP\u3002\u5728\u53ef\u80fd\u7684\u60c5\u51b5\u4e0b\uff0c\u8fd8\u5e94\u5c06 Cookie \u6807\u8bb0\u4e3a SameSite=strict\u3002\u6709\u5173\u8be6\u7ec6\u4fe1\u606f\uff0c\u8bf7\u53c2\u9605\u6587\u6863\uff1a<a href=\"http:\/\/mng.bz\/a11m\">http:\/\/mng.bz\/a11m<\/a>\u3002<\/p>\n<p>\u2022  Don\u2019t expose whether a user is already registered with your app. Leaking this information can expose you to enumeration attacks.<br \/>\n\u4e0d\u8981\u66b4\u9732\u7528\u6237\u662f\u5426\u5df2\u5728\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u6ce8\u518c\u3002\u6cc4\u9732\u6b64\u4fe1\u606f\u53ef\u80fd\u4f1a\u4f7f\u60a8\u9762\u4e34\u679a\u4e3e\u653b\u51fb\u3002<\/p>\n<p><b>TIP<\/b>  You can learn more about website enumeration in this video tutorial by Troy Hunt: <a href=\"http:\/\/mng.bz\/PAAA\">http:\/\/mng.bz\/PAAA<\/a>.<br \/>\n\u63d0\u793a:\u60a8\u53ef\u4ee5\u5728 Troy Hunt \u63d0\u4f9b\u7684\u6b64\u89c6\u9891\u6559\u7a0b\u4e2d\u4e86\u89e3\u6709\u5173\u7f51\u7ad9\u679a\u4e3e\u7684\u66f4\u591a\u4fe1\u606f\uff1a<a href=\"http:\/\/mng.bz\/PAAA\">http:\/\/mng.bz\/PAAA<\/a>\u3002<\/p>\n<p>These guidelines represent the minimum you should be doing to protect your users. The most important thing is to be aware of potential security problems as you\u2019re building your app. Trying to bolt on security at the end is always harder than thinking about it from the start, so it\u2019s best to think about it earlier rather than later.<br \/>\n\u8fd9\u4e9b\u51c6\u5219\u4ee3\u8868\u4e86\u4e3a\u4fdd\u62a4\u7528\u6237\u800c\u5e94\u91c7\u53d6\u7684\u6700\u4f4e\u9650\u5ea6\u4f5c\u3002\u6700\u91cd\u8981\u7684\u662f\u5728\u6784\u5efa\u5e94\u7528\u7a0b\u5e8f\u65f6\u4e86\u89e3\u6f5c\u5728\u7684\u5b89\u5168\u95ee\u9898\u3002\u8bd5\u56fe\u5728\u6700\u540e\u52a0\u5f3a\u5b89\u5168\u6027\u603b\u662f\u6bd4\u4ece\u4e00\u5f00\u59cb\u5c31\u8003\u8651\u5b83\u66f4\u96be\uff0c\u56e0\u6b64\u6700\u597d\u5c3d\u65e9\u8003\u8651\u800c\u4e0d\u662f\u665a\u70b9\u8003\u8651\u3002<\/p>\n<p>This chapter has been a whistle-stop tour of things to look out for. We\u2019ve touched on most of the big names in security vulnerabilities, but I strongly encourage you to check out the other resources mentioned in this chapter. They provide a more exhaustive list of things to consider, complementing the defenses mentioned in this chapter. On top of that, don\u2019t forget about input validation and mass assignment\/overposting, as discussed in chapter 16. ASP.NET Core includes basic protections against some of the most common attacks, but you can still shoot yourself in the foot. Make sure it\u2019s not your app making headlines for being breached!<br \/>\n\u672c\u7ae0\u662f\u5bf9\u9700\u8981\u6ce8\u610f\u7684\u4e8b\u9879\u7684\u7b80\u8981\u4ecb\u7ecd\u3002\u6211\u4eec\u5df2\u7ecf\u89e6\u53ca\u4e86\u5b89\u5168\u6f0f\u6d1e\u4e2d\u7684\u5927\u591a\u6570\u77e5\u540d\u4e13\u5bb6\uff0c\u4f46\u6211\u5f3a\u70c8\u5efa\u8bae\u60a8\u67e5\u770b\u672c\u7ae0\u4e2d\u63d0\u5230\u7684\u5176\u4ed6\u8d44\u6e90\u3002\u5b83\u4eec\u63d0\u4f9b\u4e86\u66f4\u8be6\u5c3d\u7684\u9700\u8981\u8003\u8651\u7684\u4e8b\u9879\u5217\u8868\uff0c\u4ee5\u8865\u5145\u672c\u7ae0\u4e2d\u63d0\u5230\u7684\u9632\u5fa1\u63aa\u65bd\u3002\u6700\u91cd\u8981\u7684\u662f\uff0c\u4e0d\u8981\u5fd8\u8bb0 input validation \u548c mass assignment \/ overposting\uff0c\u5982 Chapter 16 \u6240\u8ff0\u3002ASP.NET Core \u5305\u62ec\u9488\u5bf9\u4e00\u4e9b\u6700\u5e38\u89c1\u653b\u51fb\u7684\u57fa\u672c\u4fdd\u62a4\uff0c\u4f46\u60a8\u4ecd\u7136\u53ef\u4ee5\u642c\u8d77\u77f3\u5934\u7838\u81ea\u5df1\u7684\u811a\u3002\u786e\u4fdd\u4e0d\u662f\u60a8\u7684\u5e94\u7528\u56e0\u88ab\u6cc4\u9732\u800c\u6210\u4e3a\u5934\u6761\u65b0\u95fb\uff01<\/p>\n<h2>29.5 Summary<\/h2>\n<p>29.5 \u603b\u7ed3<\/p>\n<p>XSS attacks involve malicious users injecting content into your app, typically to run malicious JavaScript when users browse your app. You can prevent XSS injection attacks by always encoding unsafe input before writing it to a page. Razor Pages do this automatically unless you use the @Html.Raw() method, so use it sparingly and carefully.<br \/>\nXSS \u653b\u51fb\u6d89\u53ca\u6076\u610f\u7528\u6237\u5c06\u5185\u5bb9\u6ce8\u5165\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\uff0c\u901a\u5e38\u662f\u5728\u7528\u6237\u6d4f\u89c8\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u65f6\u8fd0\u884c\u6076\u610f JavaScript\u3002\u60a8\u53ef\u4ee5\u901a\u8fc7\u5728\u5c06\u4e0d\u5b89\u5168\u7684\u8f93\u5165\u5199\u5165\u9875\u9762\u4e4b\u524d\u59cb\u7ec8\u5bf9\u5176\u8fdb\u884c\u7f16\u7801\u6765\u9632\u6b62 XSS \u6ce8\u5165\u653b\u51fb\u3002\u9664\u975e\u60a8\u4f7f\u7528 @Html.Raw\uff08\uff09 \u65b9\u6cd5\uff0c\u5426\u5219 Razor Pages \u4f1a\u81ea\u52a8\u6267\u884c\u6b64\u4f5c\uff0c\u56e0\u6b64\u8bf7\u8c28\u614e\u4f7f\u7528\u3002<\/p>\n<p>CSRF attacks are a problem for apps that use cookie-based authentication, such as ASP.NET Core Identity. These attacks rely on the fact that browsers automatically send cookies to a website. A malicious website could create a form that POSTs to your site, and the browser will send the authentication cookie with the request. This allows malicious websites to send requests as though they\u2019re the logged-in user.<br \/>\nCSRF \u653b\u51fb\u5bf9\u4e8e\u4f7f\u7528\u57fa\u4e8e Cookie \u7684\u8eab\u4efd\u9a8c\u8bc1\uff08\u4f8b\u5982 ASP.NET Core Identity\uff09\u7684\u5e94\u7528\u7a0b\u5e8f\u6765\u8bf4\u662f\u4e00\u4e2a\u95ee\u9898\u3002\u8fd9\u4e9b\u653b\u51fb\u4f9d\u8d56\u4e8e\u6d4f\u89c8\u5668\u81ea\u52a8\u5411\u7f51\u7ad9\u53d1\u9001 cookie \u7684\u4e8b\u5b9e\u3002\u6076\u610f\u7f51\u7ad9\u53ef\u80fd\u4f1a\u521b\u5efa\u4e00\u4e2a\u8868\u5355\uff0c\u8be5\u8868\u5355\u5c06 POST \u5230\u60a8\u7684\u7f51\u7ad9\uff0c\u5e76\u4e14\u6d4f\u89c8\u5668\u4f1a\u5c06\u8eab\u4efd\u9a8c\u8bc1 Cookie \u4e0e\u8bf7\u6c42\u4e00\u8d77\u53d1\u9001\u3002\u8fd9\u5141\u8bb8\u6076\u610f\u7f51\u7ad9\u50cf\u767b\u5f55\u7528\u6237\u4e00\u6837\u53d1\u9001\u8bf7\u6c42\u3002<\/p>\n<p>You can mitigate CSRF attacks using antiforgery tokens, which involve writing a hidden \ufb01eld in every form that contains a random string based on the current user. A similar token is stored in a cookie. A legitimate request will have both parts, but a forged request from a malicious website will have only the cookie half; it cannot re-create the hidden \ufb01eld in the form. By validating these tokens, your API can reject forged requests.<br \/>\n\u60a8\u53ef\u4ee5\u4f7f\u7528\u9632\u4f2a\u4ee4\u724c\u7f13\u89e3 CSRF \u653b\u51fb\uff0c\u8fd9\u6d89\u53ca\u4ee5\u6bcf\u79cd\u5f62\u5f0f\u7f16\u5199\u4e00\u4e2a\u9690\u85cf\u5b57\u6bb5\uff0c\u5176\u4e2d\u5305\u542b\u57fa\u4e8e\u5f53\u524d\u7528\u6237\u7684\u968f\u673a\u5b57\u7b26\u4e32\u3002\u7c7b\u4f3c\u7684\u4ee4\u724c\u5b58\u50a8\u5728 Cookie \u4e2d\u3002\u5408\u6cd5\u8bf7\u6c42\u5c06\u5305\u542b\u4e24\u4e2a\u90e8\u5206\uff0c\u4f46\u6765\u81ea\u6076\u610f\u7f51\u7ad9\u7684\u4f2a\u9020\u8bf7\u6c42\u5c06\u53ea\u6709 cookie \u7684\u4e00\u534a;\u5b83\u65e0\u6cd5\u5728\u8868\u5355\u4e2d\u91cd\u65b0\u521b\u5efa\u9690\u85cf\u5b57\u6bb5\u3002\u901a\u8fc7\u9a8c\u8bc1\u8fd9\u4e9b\u4ee4\u724c\uff0c\u60a8\u7684 API \u53ef\u4ee5\u62d2\u7edd\u4f2a\u9020\u7684\u8bf7\u6c42\u3002<\/p>\n<p>The Razor Pages framework automatically adds antiforgery tokens to any forms you create using Razor and validates the tokens for inbound requests. You can disable the validation check if necessary, using the [IgnoreAntiForgeryToken] attribute.<br \/>\nRazor Pages \u6846\u67b6\u4f1a\u81ea\u52a8\u5c06\u9632\u4f2a\u4ee4\u724c\u6dfb\u52a0\u5230\u60a8\u4f7f\u7528 Razor \u521b\u5efa\u7684\u4efb\u4f55\u8868\u5355\u4e2d\uff0c\u5e76\u9a8c\u8bc1\u5165\u7ad9\u8bf7\u6c42\u7684\u4ee4\u724c\u3002\u5982\u6709\u5fc5\u8981\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528 [IgnoreAntiForgeryToken] \u5c5e\u6027\u7981\u7528\u9a8c\u8bc1\u68c0\u67e5\u3002<\/p>\n<p>Browsers won\u2019t allow websites to make JavaScript AJAX requests from one app to others at di\ufb00erent origins. To match the origin, the app must have the same scheme, domain, and port. If you wish to make cross-origin requests like this, you must enable CORS in your API.<br \/>\n\u6d4f\u89c8\u5668\u4e0d\u5141\u8bb8\u7f51\u7ad9\u4ece\u4e00\u4e2a\u5e94\u7528\u7a0b\u5e8f\u5411\u4e0d\u540c\u6765\u6e90\u7684\u5176\u4ed6\u5e94\u7528\u7a0b\u5e8f\u53d1\u51fa JavaScript AJAX \u8bf7\u6c42\u3002\u8981\u5339\u914d\u6e90\uff0c\u5e94\u7528\u7a0b\u5e8f\u5fc5\u987b\u5177\u6709\u76f8\u540c\u7684 scheme\u3001domain \u548c port\u3002\u5982\u679c\u60a8\u5e0c\u671b\u53d1\u51fa\u8fd9\u6837\u7684\u8de8\u57df\u8bf7\u6c42\uff0c\u5219\u5fc5\u987b\u5728 API \u4e2d\u542f\u7528 CORS\u3002<\/p>\n<p>CORS uses HTTP headers to communicate with browsers and de\ufb01nes which origins can call your API. In ASP.NET Core, you can de\ufb01ne multiple policies, which can be applied globally to your whole app or to speci\ufb01c controllers and actions.<br \/>\nCORS \u4f7f\u7528 HTTP \u6807\u5934\u4e0e\u6d4f\u89c8\u5668\u901a\u4fe1\uff0c\u5e76\u5b9a\u4e49\u54ea\u4e9b\u6e90\u53ef\u4ee5\u8c03\u7528\u60a8\u7684 API\u3002\u5728 ASP.NET Core \u4e2d\uff0c\u60a8\u53ef\u4ee5\u5b9a\u4e49\u591a\u4e2a\u7b56\u7565\uff0c\u8fd9\u4e9b\u7b56\u7565\u53ef\u4ee5\u5168\u5c40\u5e94\u7528\u4e8e\u6574\u4e2a\u5e94\u7528\u7a0b\u5e8f\u6216\u7279\u5b9a\u63a7\u5236\u5668\u548c\u4f5c\u3002<\/p>\n<p>You can add the CORS middleware by calling UseCors() on WebApplication and optionally providing the name of the default CORS policy to apply. You can also apply CORS to endpoints by calling RequireCors() or adding the [EnableCors] attribute and providing the name of the policy to apply.<br \/>\n\u60a8\u53ef\u4ee5\u901a\u8fc7\u5728 WebApplication \u4e0a\u8c03\u7528 UseCors\uff08\uff09 \u5e76\u9009\u62e9\u6027\u5730\u63d0\u4f9b\u8981\u5e94\u7528\u7684\u9ed8\u8ba4 CORS \u7b56\u7565\u7684\u540d\u79f0\u6765\u6dfb\u52a0 CORS \u4e2d\u95f4\u4ef6\u3002\u60a8\u8fd8\u53ef\u4ee5\u901a\u8fc7\u8c03\u7528 RequireCors\uff08\uff09 \u6216\u6dfb\u52a0 [EnableCors] \u5c5e\u6027\u5e76\u63d0\u4f9b\u8981\u5e94\u7528\u7684\u7b56\u7565\u7684\u540d\u79f0\uff0c\u5c06 CORS \u5e94\u7528\u4e8e\u7ec8\u7aef\u8282\u70b9\u3002<\/p>\n<p>Con\ufb01gure the policies for your application by calling AddCors() on WebApplicationBuilder and adding policies in the lambda using AddPolicy(). A policy de\ufb01nes which origins are allowed to call an endpoint, which HTTP methods they can use, and which headers are allowed.<br \/>\n\u901a\u8fc7\u5728 WebApplicationBuilder \u4e0a\u8c03\u7528 AddCors\uff08\uff09 \u5e76\u4f7f\u7528 AddPolicy\uff08\uff09 \u5728 lambda \u4e2d\u6dfb\u52a0\u7b56\u7565\u6765\u914d\u7f6e\u5e94\u7528\u7a0b\u5e8f\u7684\u7b56\u7565\u3002\u7b56\u7565\u5b9a\u4e49\u5141\u8bb8\u54ea\u4e9b\u6e90\u8c03\u7528\u7ec8\u7aef\u8282\u70b9\u3001\u5b83\u4eec\u53ef\u4ee5\u4f7f\u7528\u54ea\u4e9b HTTP \u65b9\u6cd5\u4ee5\u53ca\u5141\u8bb8\u54ea\u4e9b\u6807\u5934\u3002<\/p>\n<p>Open redirect attacks use the common returnURL mechanism after logging in to redirect users to malicious websites. You can prevent this attack by ensuring that you redirect only to local URLs\u2014URLs that belong to your app.<br \/>\nOpen \u91cd\u5b9a\u5411\u653b\u51fb\u5728\u767b\u5f55\u540e\u4f7f\u7528\u5e38\u89c1\u7684 returnURL \u673a\u5236\u5c06\u7528\u6237\u91cd\u5b9a\u5411\u5230\u6076\u610f\u7f51\u7ad9\u3002\u60a8\u53ef\u4ee5\u901a\u8fc7\u786e\u4fdd\u4ec5\u91cd\u5b9a\u5411\u5230\u672c\u5730 URL\uff08\u5c5e\u4e8e\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u7684 URL\uff09\u6765\u9632\u6b62\u6b64\u653b\u51fb\u3002<\/p>\n<p>Insecure direct object references are a common problem where you expose the ID of database entities in the URL. You should always verify that users have permission to access or change the requested resource by using resource-based authorization in your action methods.<br \/>\n\u4e0d\u5b89\u5168\u7684\u76f4\u63a5\u5bf9\u8c61\u5f15\u7528\u662f\u4e00\u4e2a\u5e38\u89c1\u95ee\u9898\uff0c\u5373\u5728 URL \u4e2d\u516c\u5f00\u6570\u636e\u5e93\u5b9e\u4f53\u7684 ID\u3002\u60a8\u5e94\u8be5\u59cb\u7ec8\u901a\u8fc7\u5728\u4f5c\u65b9\u6cd5\u4e2d\u4f7f\u7528\u57fa\u4e8e\u8d44\u6e90\u7684\u6388\u6743\u6765\u9a8c\u8bc1\u7528\u6237\u662f\u5426\u6709\u6743\u8bbf\u95ee\u6216\u66f4\u6539\u8bf7\u6c42\u7684\u8d44\u6e90\u3002<\/p>\n<p>SQL injection attacks are a common attack vector when you build SQL requests manually. Always use parameterized queries when building requests or use a framework like EF Core, which isn\u2019t vulnerable to SQL injection.<br \/>\n\u5f53\u60a8\u624b\u52a8\u6784\u5efa SQL \u8bf7\u6c42\u65f6\uff0cSQL \u6ce8\u5165\u653b\u51fb\u662f\u4e00\u79cd\u5e38\u89c1\u7684\u653b\u51fb\u5a92\u4ecb\u3002\u5728\u751f\u6210\u8bf7\u6c42\u65f6\uff0c\u8bf7\u59cb\u7ec8\u4f7f\u7528\u53c2\u6570\u5316\u67e5\u8be2\uff0c\u6216\u4f7f\u7528 EF Core \u7b49\u6846\u67b6\uff0c\u8be5\u6846\u67b6\u4e0d\u6613\u53d7\u5230 SQL \u6ce8\u5165\u7684\u653b\u51fb\u3002<\/p>\n<p>The most sensitive data in your app is often the data of your users. Mitigate this risk by storing only data that you need. Ensure that you store passwords only as a hash, protect against weak or compromised passwords, and provide the option for MFA. ASP.NET Core Identity provides all of this out of the box, so it\u2019s a great choice if you need to create an identity provider.<br \/>\n\u5e94\u7528\u7a0b\u5e8f\u4e2d\u6700\u654f\u611f\u7684\u6570\u636e\u901a\u5e38\u662f\u7528\u6237\u7684\u6570\u636e\u3002\u901a\u8fc7\u4ec5\u5b58\u50a8\u60a8\u9700\u8981\u7684\u6570\u636e\u6765\u964d\u4f4e\u6b64\u98ce\u9669\u3002\u786e\u4fdd\u4ec5\u5c06\u5bc6\u7801\u5b58\u50a8\u4e3a\u54c8\u5e0c\u503c\uff0c\u9632\u6b62\u5f31\u5bc6\u7801\u6216\u6cc4\u9732\u5bc6\u7801\uff0c\u5e76\u63d0\u4f9b MFA \u9009\u9879\u3002ASP.NET Core Identity \u63d0\u4f9b\u4e86\u6240\u6709\u8fd9\u4e9b\u5f00\u7bb1\u5373\u7528\u7684\u529f\u80fd\uff0c\u56e0\u6b64\u5982\u679c\u60a8\u9700\u8981\u521b\u5efa\u8eab\u4efd\u63d0\u4f9b\u5546\uff0c\u5b83\u662f\u4e00\u4e2a\u4e0d\u9519\u7684\u9009\u62e9\u3002<\/p>\n<ol>\n<li>In 2020 the National Institute of Standards and Technology (NIST) updated its Digital Identity Guidelines on handling user details, which contains some great advice. See <a href=\"http:\/\/mng.bz\/6gRA\">http:\/\/mng.bz\/6gRA<\/a>.<\/li>\n<li>2020 \u5e74\uff0c\u7f8e\u56fd\u56fd\u5bb6\u6807\u51c6\u4e0e\u6280\u672f\u7814\u7a76\u9662 \uff08NIST\uff09 \u66f4\u65b0\u4e86\u5173\u4e8e\u5904\u7406\u7528\u6237\u8be6\u7ec6\u4fe1\u606f\u7684\u6570\u5b57\u8eab\u4efd\u6307\u5357\uff0c\u5176\u4e2d\u5305\u542b\u4e00\u4e9b\u5f88\u597d\u7684\u5efa\u8bae\u3002\u8bf7\u53c2\u9605 <a href=\"http:\/\/mng.bz\/6gRA\">http:\/\/mng.bz\/6gRA<\/a>\u3002<\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>29 Improving your application\u2019s security 29 \u63d0\u9ad8\u5e94\u7528\u7a0b\u5e8f\u7684\u5b89\u5168\u6027 This chapter covers \u672c\u7ae0\u6db5\u76d6 \u2022 Defending against cross-site scripting attacks \u9632\u5fa1\u8de8\u7ad9\u70b9\u811a\u672c\u653b\u51fb \u2022 Protecting from cross-site request forgery attacks \u9632\u6b62\u8de8\u7ad9\u70b9\u8bf7\u6c42\u4f2a\u9020\u653b\u51fb \u2022 Allowing calls to your API from other apps using CORS \u5141\u8bb8\u4f7f\u7528 CORS\u4ece\u5176\u4ed6\u5e94\u7528\u7a0b\u5e8f\u8c03\u7528\u60a8\u7684 API \u2022 Avoiding attach vectors such as SQL injection attacks \u907f\u514d SQL \u6ce8\u5165\u653b\u51fb\u7b49\u9644\u52a0\u5411\u91cf In chapter 28 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":["post-629","post","type-post","status-publish","format-standard","hentry","category-csharp"],"_links":{"self":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/629","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=629"}],"version-history":[{"count":0,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/629\/revisions"}],"wp:attachment":[{"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=629"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=629"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=629"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}