ЁЯЦНя╕П рдзрдбрд╛ 02 тАФ XSS рдЖрдгрд┐ Content-Security-Policy: code рдореНрд╣рдгреВрди рдЪрд╛рд▓рдгрд╛рд░реА comment
ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 16 рдкреИрдХреА рдзрдбрд╛ 02 ┬╖ рдорд╛рдЧреЗ: lesson-01-injection ┬╖ рдкреБрдвреЗ: lesson-03-browser-server
ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ
рдзрдбрд╛ 01, рдЖрдгрд┐ рдЕрд░реНрдЬ рдЦрд┐рдбрдХреАрд╡рд░рдЪреА рджреБрд╕рд░реА рдХрдордХреБрд╡рдд рдЬрд╛рдЧрд╛: cross-site scripting (XSS).
рд╢рд╛рд│реЗрдЪреНрдпрд╛ notice board рд╡рд░рдЪреНрдпрд╛ рдПрдХрд╛ comment рдордзреНрдпреЗ <script> tag рдЖрд╣реЗ. рдЬрд╢реАрдЪреНрдпрд╛ рддрд╢реА print рдХреЗрд▓реА рддрд░ рддреА
рдкреНрд░рддреНрдпреЗрдХ рд╡рд╛рдЪрдХрд╛рдЪреНрдпрд╛ browser рдордзреНрдпреЗ рдЪрд╛рд▓рддреЗ. Escape рдХрд░реВрди print рдХреЗрд▓реА рддрд░ рддреА рдлрдХреНрдд text рдЕрд╕рддреЗ. рдордЧ
Content-Security-Policy (CSP) тАФ browser рдЪреА рд╕реНрд╡рддрдГрдЪреА рдирд┐рдпрдорд╛рдВрдЪреА рдпрд╛рджреА тАФ рдирд┐рд╕рдЯреВрди рдЖрд▓реЗрд▓реНрдпрд╛ scripts block рдХрд░рддреЗ.
sec/demo.py рдордзрд▓реЗ xss() рджреЛрдиреНрд╣реА рджрд╛рдЦрд╡рддреЗ.
ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ
рд╢рд╛рд│реЗрдд рдПрдХ notice board рдЖрд╣реЗ. рдХреЛрдгреАрд╣реА рдЪрд┐рдареНрдареА рд▓рд╛рд╡реВ рд╢рдХрддреЛ. Security office рдкреНрд░рддреНрдпреЗрдХ рдЪрд┐рдареНрдареА рд╣рд╛рддрд╛рдиреЗ рдореЛрдареНрдпрд╛ board рд╡рд░ рдЙрддрд░рд╡реВрди рдШреЗрддреЗ.
рдПрдХрд╛ рдЪрд┐рдареНрдареАрдд рд▓рд┐рд╣рд┐рд▓реЗ рдЖрд╣реЗ: "рдЙрддреНрддрдо рдирд┐рдХрд╛рд▓! тАФ рдЖрдгрд┐ рдЖрддрд╛, рд╣реЗ рд╡рд╛рдЪрдгрд╛рд▒реНрдпрд╛ рдкреНрд░рддреНрдпреЗрдХрд╛рдиреЗ рдЖрдкрд▓реА bag рдЙрдШрдбреВрди рдЖрдкрд▓реЗ рдкреНрд░рдЧрддреАрдкреБрд╕реНрддрдХ рдорд▓рд╛ рджреНрдпрд╛рд╡реЗ." рдЬрд░ office рдиреЗ рддреА рдЖрджреЗрд╢ рдореНрд╣рдгреВрди рдЙрддрд░рд╡рд▓реА, рддрд░ рдкреНрд░рддреНрдпреЗрдХ рд╡рд╛рдЪрдХ рддреА рдкрд╛рд│рддреЛ.
рдореНрд╣рдгреВрди office рдкреНрд░рддреНрдпреЗрдХ рдЪрд┐рдареНрдареА рдЕрд╡рддрд░рдг рдЪрд┐рдиреНрд╣рд╛рдВрдЪреНрдпрд╛ рдЖрдд, рд╡реЗрдЧрд│реНрдпрд╛ рд░рдВрдЧрд╛рдд рдЙрддрд░рд╡рддреЗ. рдЖрддрд╛ рд╡рд╛рдЪрдХрд╛рдВрдирд╛ рд╢рдмреНрдж рджрд┐рд╕рддрд╛рдд, рдкрдг рддреНрдпрд╛рдВрдирд╛ рдХрд│рддреЗ: "рд╣реЗ рдХреЛрдгреАрддрд░реА рд▓рд┐рд╣рд┐рд▓реЗрд▓реЗ рдЖрд╣реЗ, рд╢рд╛рд│реЗрдЪрд╛ рдЖрджреЗрд╢ рдирд╛рд╣реА". рдпрд╛рд▓рд╛рдЪ output рд╡реЗрд│реА escaping рдореНрд╣рдгрддрд╛рдд.
рдЖрдгрд┐ office рджреБрд╕рд░рд╛ рдирд┐рдпрдо рдЬреЛрдбрддреЗ, board рдЪреНрдпрд╛ рд╡рд░ рдЫрд╛рдкрд▓реЗрд▓рд╛: "рдлрдХреНрдд рдореБрдЦреНрдп office рдиреЗ рд╕рд╣реА рдХреЗрд▓реЗрд▓реЗ рдЖрджреЗрд╢рдЪ рдЧреНрд░рд╛рд╣реНрдп." рдПрдЦрд╛рджреА рдЪрд┐рдареНрдареА рдЕрд╡рддрд░рдг рдЪрд┐рдиреНрд╣рд╛рдВрд╢рд┐рд╡рд╛рдп рдирд┐рд╕рдЯрд▓реА рддрд░реА рдХреЛрдгреА рддреА рдкрд╛рд│рдд рдирд╛рд╣реА тАФ рддрд┐рдЪреНрдпрд╛рд╡рд░ рд╕рд╣реА рдирд╕рддреЗ. рддреЛ рдирд┐рдпрдо рдореНрд╣рдгрдЬреЗ Content-Security-Policy. рддреЗ рдПрдХ рд╕реБрд░рдХреНрд╖рд╛ рдЬрд╛рд│реЗ рдЖрд╣реЗ. рдЕрд╡рддрд░рдг рдЪрд┐рдиреНрд╣реЗ рд╣рд╛рдЪ рдЦрд░рд╛ рдЙрдкрд╛рдп рдЖрд╣реЗ.
ЁЯЧ║я╕П рдЖрдХреГрддреА
flowchart LR
c["ЁЯТм comment<br/>Great results! + a script tag"]
c --> raw["тЭМ printed raw<br/>the tag stays a tag"]
c --> esc["тЬЕ printed escaped<br/>angle brackets become text"]
raw --> b1["ЁЯМР reader's browser<br/>runs the script"]
esc --> b2["ЁЯМР reader's browser<br/>shows the words"]
csp["ЁЯЫбя╕П CSP header<br/>script-src 'self' cdn.school.example"] -.->|"safety net: inline blocked"| b1
ЁЯЧ║я╕П рдХрд╛рдврд▓реЗрд▓реА рдЖрд╡реГрддреНрддреА + рдПрдХ lab: https://school-edh.pages.dev/security/lesson-diagrams.html#l02
тЭУ рдХрд╛рдп
- XSS (cross-site scripting) тАФ рдПрдХрд╛ рд╡реНрдпрдХреНрддреАрдЪрд╛ input рджреБрд╕рд▒реНрдпрд╛ рд╡реНрдпрдХреНрддреАрдЪреНрдпрд╛ page рд╡рд░ рдкреЛрд╣реЛрдЪрддреЛ рдЖрдгрд┐
рддрд┐рдереЗ script рдореНрд╣рдгреВрди рдЪрд╛рд▓рддреЛ. Script рд╡рд╛рдЪрдХ рдореНрд╣рдгреВрди рдЪрд╛рд▓рддреЗ: рддреА page рд╡рд╛рдЪреВ рд╢рдХрддреЗ, рд╡рд╛рдЪрдХрд╛рдЪреНрдпрд╛ session рдиреЗ
API рд▓рд╛ call рдХрд░реВ рд╢рдХрддреЗ рдЖрдгрд┐ рд╡рд╛рдЪрдХрд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рддреЗ рддреЗ рдмрджрд▓реВ рд╢рдХрддреЗ. OWASP Top 10 (2021) рддреЗ
A03:2021 тАУ Injection рдЦрд╛рд▓реА рдореЛрдЬрддреЗ.
- Stored тАФ рд╕рд╛рдард╡рд▓реЗрд▓реЗ (рдПрдЦрд╛рджреА comment) рдЖрдгрд┐ page рдЙрдШрдбрдгрд╛рд▒реНрдпрд╛ рдкреНрд░рддреНрдпреЗрдХрд╛рд▓рд╛ рджрд╛рдЦрд╡рд▓реЗрд▓реЗ.
- Reflected тАФ link рдордзреВрди рдкрд╛рдард╡рд▓реЗрд▓реЗ рдЖрдгрд┐ рдПрдХрджрд╛ рдкрд░рдд рджрд╛рдЦрд╡рд▓реЗрд▓реЗ.
- DOM-based тАФ page рдЪреЗрдЪ JavaScript input page рдордзреНрдпреЗ рдШрд╛рд▓рддреЗ (
innerHTML).
- Output encoding (escaping) тАФ print рдХрд░рддрд╛рдирд╛ рдЦрд╛рд╕ рдЪрд┐рдиреНрд╣реЗ рдирд┐рд░реБрдкрджреНрд░рд╡реА text рдордзреНрдпреЗ рдмрджрд▓рд╛:
<тЖТ<,"тЖТ".render_safe()html.escape()рд╡рд╛рдкрд░рддреЗ. - Context-aware тАФ рдпреЛрдЧреНрдп encoding value рдХреБрдареЗ рдЬрд╛рддреЗ рддреНрдпрд╛рд╡рд░ рдЕрд╡рд▓рдВрдмреВрди рдЕрд╕рддреЗ: HTML text,
HTML attribute, JavaScript string, URL, CSS. HTML escaping рдореБрд│реЗ value
<script>рдЪреНрдпрд╛ рдЖрдд рдХрд┐рдВрд╡рд╛href="javascript:тАж"рдЪреНрдпрд╛ рдЖрдд рд╕реБрд░рдХреНрд╖рд┐рдд рд╣реЛрдд рдирд╛рд╣реА. Auto-escaping рдЕрд╕рд▓реЗрд▓реА template engines (Jinja2, Django templates, React JSX, Angular, Razor) HTML text рдЖрдгрд┐ attributes рддреБрдордЪреНрдпрд╛рд╕рд╛рдареА рд╕рд╛рдВрднрд╛рд│рддрд╛рдд. - Sanitising тАФ рдЬреЗрд╡реНрд╣рд╛ users рдирд╛ HTML (rich text) рдкрд╛рдард╡рд╛рд╡реЗрдЪ рд▓рд╛рдЧрддреЗ, рддреЗрд╡реНрд╣рд╛ DOMPurify рд╕рд╛рд░рдЦрд╛ sanitiser рдлрдХреНрдд рд╕реБрд░рдХреНрд╖рд┐рдд tags рдЖрдгрд┐ attributes рдЪреА allow-list рдареЗрд╡рддреЛ.
- Content-Security-Policy (CSP) тАФ HTTP response header рдЬреЛ browser рд▓рд╛ рд╕рд╛рдВрдЧрддреЛ рдХреЛрдгрддреНрдпрд╛
scripts рдЪрд╛рд▓реВ рд╢рдХрддрд╛рдд. рдореБрдЦреНрдп рднрд╛рдЧ:
script-srcтАФ scripts рдХреБрдареВрди рдпреЗрдК рд╢рдХрддрд╛рдд ('self', рдирд╛рд╡ рджрд┐рд▓реЗрд▓реЗ hosts). рдирд╕реЗрд▓ рддрд░default-srcрд╡рд╛рдкрд░рд▓рд╛ рдЬрд╛рддреЛ.'unsafe-inline'рдирд╛рд╣реА тАФ рддреЗ рдирд╕реЗрд▓ рддрд░ inline<script>blocks рдЖрдгрд┐onerror=handlers рдЪрд╛рд▓рдд рдирд╛рд╣реАрдд.- nonces рдХрд┐рдВрд╡рд╛ hashes тАФ рддреБрдордЪреА рд╕реНрд╡рддрдГрдЪреА inline script рдЪрд╛рд▓реВ рджреЗрдгреНрдпрд╛рд╕рд╛рдареА рддрд┐рд▓рд╛ рдкреНрд░рддреНрдпреЗрдХ response рд╕рд╛рдареА random
nonce-тАжрджреНрдпрд╛ рдХрд┐рдВрд╡рд╛ рддрд┐рдЪрд╛sha256-тАжhash рдпрд╛рджреАрдд рд▓рд┐рд╣рд╛. рдШреБрд╕рд╡рд▓реЗрд▓реНрдпрд╛ script рдХрдбреЗ рдпрд╛рддрд▓реЗ рдХрд╛рд╣реАрдЪ рдирд╕рддреЗ. object-src 'none',base-uri 'none',frame-ancestors 'none'рдЗрддрд░ рджрд╛рд░реЗ рдмрдВрдж рдХрд░рддрд╛рдд.
- CSP рд╣реЗ рд╕реБрд░рдХреНрд╖рд╛ рдЬрд╛рд│реЗ рдЖрд╣реЗ, рдЙрдкрд╛рдп рдирд╛рд╣реА тАФ рдХрдбрдХ CSP рдЪреБрдХрд▓реЗрд▓реНрдпрд╛ escape рдЪреЗ рдиреБрдХрд╕рд╛рди рдорд░реНрдпрд╛рджрд┐рдд рдХрд░рддреЗ. рдЙрдкрд╛рдп рдЕрдЬреВрдирд╣реА рдпреЛрдЧреНрдп encoding рд╣рд╛рдЪ рдЖрд╣реЗ.
ЁЯдФ рдХрд╛
рдХрд╛рд░рдг notice board рд╕рдЧрд│реНрдпрд╛рдВрдЪрд╛ рдЖрд╣реЗ. рдПрдХ рд╕рд╛рдард╡рд▓реЗрд▓реА comment рдкреНрд░рддреНрдпреЗрдХ рдкрд╛рд▓рдХрд╛рдЪреНрдпрд╛ browser рдордзреНрдпреЗ,
рддреНрдпрд╛рдВрдЪреНрдпрд╛ login рдиреЗ рдЪрд╛рд▓реВ рд╢рдХрддреЗ. Script рддреНрдпрд╛рдВрдЪреНрдпрд╛ рдирд╛рд╡рд╛рдиреЗ /api/grades рд▓рд╛ call рдХрд░реВ рд╢рдХрддреЗ, рдХрд┐рдВрд╡рд╛
"fees рднрд░рд╛" link рдмрджрд▓реВ рд╢рдХрддреЗ. Server рд▓рд╛ рдХрд╛рд╣реАрдЪ рд╡рд┐рдЪрд┐рддреНрд░ рджрд┐рд╕рдд рдирд╛рд╣реА тАФ рдкреНрд░рддреНрдпреЗрдХ request рд╕реЛрдмрдд рдЦрд░реЗ session рдЕрд╕рддреЗ.
Output рд╡реЗрд│реА escaping comment рд▓рд╛ code рдмрдирдгреНрдпрд╛рдкрд╛рд╕реВрди рдерд╛рдВрдмрд╡рддреЗ. CSP рдЦрд╛рддреНрд░реА рдХрд░рддреЗ рдХреА рдПрдЦрд╛рджреА рдЪреБрдХрд▓реЗрд▓реА рдЬрд╛рдЧрд╛
рдкреВрд░реНрдг breach рдмрдирдд рдирд╛рд╣реА.
ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)
sec/web.py рдордзреНрдпреЗ render_unsafe(comment) f"<p>{comment}</p>" рдкрд░рдд рджреЗрддреЗ.
render_safe(comment) f"<p>{html.escape(comment)}</p>" рдкрд░рдд рджреЗрддреЗ.
csp_allows(policy, kind, source) рд╣рд╛ рдПрдХ рдЫреЛрдЯрд╛ CSP рд╡рд╛рдЪрдХ рдЖрд╣реЗ: рддреЛ script-src (рдХрд┐рдВрд╡рд╛
default-src) рд╢реЛрдзрддреЛ рдЖрдгрд┐ inline script, рдХрд┐рдВрд╡рд╛ source рдордзреВрди рдЖрд▓реЗрд▓реА script, рдЪрд╛рд▓реВ рд╢рдХрддреЗ рдХрд╛ рддреЗ рд╕рд╛рдВрдЧрддреЛ.
рд╣реЗ рд╢рд┐рдХрд╡рдгреНрдпрд╛рд╕рд╛рдареАрдЪреЗ model рдЖрд╣реЗ тАФ рддреЗ nonces рдХрд┐рдВрд╡рд╛ hashes рд╡рд╛рдЪрдд рдирд╛рд╣реА.
ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛
python3 sec/demo.py xss
python3 - <<'EOF'
import sys; sys.path.insert(0, "sec"); from web import render_unsafe, render_safe, csp_allows
c = '<img src=x onerror="alert(1)">'
print("raw тЖТ", render_unsafe(c))
print("escaped тЖТ", render_safe(c))
for policy in ("script-src 'self'", "script-src 'self' 'unsafe-inline'", "default-src 'self'", "script-src *"):
inline = "runs" if csp_allows(policy, "inline", None) else "blocked"
evil = "runs" if csp_allows(policy, "src", "https://evil.example") else "blocked"
print(f"{policy:<36} inline тЖТ {inline:<7} ┬╖ evil.example тЖТ {evil}")
EOF
тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ
xss рд╣реЗ print рдХрд░рддреЗ:
тФАтФА a comment on the notice board: 'Great results! <script>fetch("/api/grades")</script>'
rendered raw тЖТ <p>Great results! <script>fetch("/api/grades")</script></p>
rendered escaped тЖТ <p>Great results! <script>fetch("/api/grades")</script></p>
тФАтФА Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.school.example
inline <script>тАж</script> тЖТ blocked
src self тЖТ runs
src https://cdn.school.example тЖТ runs
src https://evil.example тЖТ blocked
escape on output for the right context (HTML, attribute, JS, URL) тАФ CSP is the safety net, not the fix
рддреБрдордЪрд╛ snippet рд╣реЗ print рдХрд░рддреЛ:
raw тЖТ <p><img src=x onerror="alert(1)"></p>
escaped тЖТ <p><img src=x onerror="alert(1)"></p>
script-src 'self' inline тЖТ blocked ┬╖ evil.example тЖТ blocked
script-src 'self' 'unsafe-inline' inline тЖТ runs ┬╖ evil.example тЖТ blocked
default-src 'self' inline тЖТ blocked ┬╖ evil.example тЖТ blocked
script-src * inline тЖТ blocked ┬╖ evil.example тЖТ runs
ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ
XSS рд▓рд╛ <script> tag рд▓рд╛рдЧрдд рдирд╛рд╣реА тАФ onerror handler рдЕрд╕рд▓реЗрд▓рд╛ <img> рдкреБрд░реЗрд╕рд╛ рдЖрд╣реЗ, рдЖрдгрд┐
escaping рджреЛрдиреНрд╣реАрдВрдирд╛ рдирд┐рд╖реНрдкреНрд░рдн рдХрд░рддреЗ. 'unsafe-inline' рдЬреЛрдбрд▓реНрдпрд╛рдиреЗ inline code рд╕рд╛рдареА рд╕реБрд░рдХреНрд╖рд╛ рдЬрд╛рд│реЗ рдмрдВрдж
рд╣реЛрддреЗ, рдЖрдгрд┐ script-src * рдореБрд│реЗ scripts рдХреЛрдгрддреНрдпрд╛рд╣реА host рд╡рд░реВрди load рд╣реЛрдК рд╢рдХрддрд╛рдд. рдЪрд╛рдВрдЧрд▓реНрдпрд╛ policy рдордзреНрдпреЗ
рдпрд╛рддрд▓реЗ рдХрд╛рд╣реАрдЪ рдирд╕рддреЗ.
тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛
- рдпреЛрдЧреНрдп context рд╕рд╛рдареА output рд╡реЗрд│реА рдХрд░рдгреНрдпрд╛рдРрд╡рдЬреА input рд╡реЗрд│реА escaping рдХрд░рдгреЗ (рдЖрдгрд┐ рдмрд┐рдШрдбрд▓реЗрд▓рд╛ text рд╕рд╛рдард╡рдгреЗ)
- user data рд╡рд░
innerHTML = userText,dangerouslySetInnerHTML,v-html, рдХрд┐рдВрд╡рд╛ Jinja рдЪреЗ|safe <script>block рдХрд┐рдВрд╡рд╛javascript:URL рдордзреНрдпреЗ рдЬрд╛рдгрд╛рд░реА value HTML-escape рдХрд░рдгреЗ- "рдлрдХреНрдд рдЪрд╛рд▓рд╛рд╡реЗ рдореНрд╣рдгреВрди"
'unsafe-inline'рдЕрд╕рд▓реЗрд▓рд╛ CSP тАФ рддреНрдпрд╛рдиреЗ рдмрд╣реБрддреЗрдХ рд╕рдВрд░рдХреНрд╖рдг рдирд┐рдШреВрди рдЬрд╛рддреЗ - test рди рдХрд░рддрд╛ рдХрдбрдХ CSP ship рдХрд░рдгреЗ тАФ
Content-Security-Policy-Report-OnlyрдиреЗ рд╕реБрд░реБрд╡рд╛рдд рдХрд░рд╛
ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд
рдЦрд▒реНрдпрд╛ account рд╡рд░ тАФ escaping framework рд▓рд╛ рдХрд░реВ рджреНрдпрд╛. Jinja2 (Flask рд╣реЗ
.html templates рд╕рд╛рдареА рдЪрд╛рд▓реВ рдХрд░рддреЗ):
from jinja2 import Environment, FileSystemLoader, select_autoescape
env = Environment(loader=FileSystemLoader("templates"), autoescape=select_autoescape(["html"]))
# {{ comment }} in a template is now escaped; never add |safe to user data
React JSX рдордзрд▓рд╛ text escape рдХрд░рддреЗ. Users рдХрдбреВрди рдЖрд▓реЗрд▓рд╛ rich text рдЖрдзреА sanitiser рдордзреВрди рдЬрд╛рддреЛ:
<p>{comment}</p> // тЬЕ escaped by React
import DOMPurify from 'dompurify';
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(richText) }} /> // only when you must
рдкреНрд░рддреНрдпреЗрдХ response рд╕рд╛рдареА nonce рдЕрд╕рд▓реЗрд▓рд╛ рдХрдбрдХ CSP (Express; helmet header set рдХрд░рддреЛ):
import crypto from 'node:crypto';
import helmet from 'helmet';
app.use((req, res, next) => { res.locals.nonce = crypto.randomBytes(16).toString('base64'); next(); });
app.use(helmet.contentSecurityPolicy({ directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", (req, res) => `'nonce-${res.locals.nonce}'`],
objectSrc: ["'none'"], baseUri: ["'none'"], frameAncestors: ["'none'"],
}}));
// in the template: <script nonce="<%= nonce %>">тАж</script>
Static site рд╕рд╛рдареА nginx рдиреЗ set рдХреЗрд▓реЗрд▓рд╛ рддреЛрдЪ header (inline scripts рдЕрдЬрд┐рдмрд╛рдд рдирд╛рд╣реАрдд):
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'" always;
ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: CSP рдЖрдзреА report-only mode рдордзреНрдпреЗ рдЖрдгрд╛, рдПрдХ рдЖрдард╡рдбрд╛ reports рд╡рд╛рдЪрд╛, рддреБрдордЪреНрдпрд╛ рд╕реНрд╡рддрдГрдЪреНрдпрд╛ inline scripts рджреБрд░реБрд╕реНрдд рдХрд░рд╛ (рддреНрдпрд╛ files рдордзреНрдпреЗ рд╣рд▓рд╡рд╛ рдХрд┐рдВрд╡рд╛ рддреНрдпрд╛рдВрдирд╛ nonce рджреНрдпрд╛), рдордЧ enforce рдХрд░рд╛. Escaping рд╣реАрдЪ рд╕рдВрд░рдХреНрд╖рдгрд╛рдЪреА рдкрд╣рд┐рд▓реА рдлрд│реА рдареЗрд╡рд╛.
тПня╕П рдкреБрдвреЗ
Browser рддреБрдордЪреА pages рдЪрд╛рд▓рд╡рддреЛ. рддреЛ рддреБрдордЪреНрдпрд╛ cookies server рдХрдбреЗ рдШреЗрдКрди рдЬрд╛рддреЛ, рдЖрдгрд┐ рддреНрдпрд╛рд▓рд╛ рдЗрддрд░ sites рдХрдбреВрди requests рдкрд╛рдард╡рд╛рдпрд▓рд╛ рд╕рд╛рдВрдЧрд┐рддрд▓реЗ рдЬрд╛рдК рд╢рдХрддреЗ. рдкреБрдвреЗ: browser рдЖрдгрд┐ server рдордзрд▓рд╛ рдЬрдкреВрди рдареЗрд╡рд▓реЗрд▓рд╛ рд╡рд┐рд╢реНрд╡рд╛рд╕ тАФ cookies, CSRF, CORS рдЖрдгрд┐ SSRF.
git checkout lesson-03-browser-server