Web Security
Network security topics, focused mainly on web security
Updated
XSS
What is XSS
Cross-Site Scripting (XSS) is a type of code injection attack. Attackers inject malicious scripts into a target website so that they run in the user's browser. With these malicious scripts, attackers can obtain the user's sensitive information such as Cookie, SessionID, etc., thereby endangering data security
Stored XSS
The attacker submits malicious code into the target website's database.
When a user opens the target website, the website server takes the malicious code out of the database, concatenates it into the HTML, and returns it to the browser.
After receiving the response, the user's browser parses and executes it, and the malicious code mixed in is also executed.
The malicious code steals user data and sends it to the attacker's website, or impersonates the user's behavior by calling the target website's APIs to perform operations specified by the attacker.
Reflected XSS
The attacker constructs a special URL that contains malicious code.
When the user opens the URL containing malicious code, the website server takes the malicious code out of the URL, concatenates it into the HTML, and returns it to the browser.
After receiving the response, the user's browser parses and executes it, and the malicious code mixed in is also executed.
The malicious code steals user data and sends it to the attacker's website, or impersonates the user's behavior by calling the target website's APIs to perform operations specified by the attacker.
DOM-based XSS
The attacker constructs a special URL that contains malicious code.
The user opens the URL containing malicious code.
After receiving the response, the user's browser parses and executes it, and the front-end JavaScript takes the malicious code out of the URL and executes it.
The malicious code steals user data and sends it to the attacker's website, or impersonates the user's behavior by calling the target website's APIs to perform operations specified by the attacker.
XSS Prevention Measures
CSP ( Content Security Policy )
Prohibit loading code from external domains, preventing complex attack logic
Prohibit submission to external domains, so that after a website is attacked, user data will not be leaked to external domains
Prohibit inline script execution (a fairly strict rule; GitHub is known to use it)
Prohibit unauthorized script execution (a new feature, used by Google Map mobile version)
Proper use of reporting can detect XSS in a timely manner, which helps fix problems as soon as possible
Input Content Length Control
For untrusted input, a reasonable length should always be enforced. Although this cannot completely prevent XSS, it can increase the difficulty of XSS attacks
Other Security Measures
- HTTP-only Cookie: prevents JavaScript from reading certain sensitive Cookies, so that even after an attacker completes an XSS injection, they cannot steal these Cookies.
- CAPTCHA: prevents scripts from impersonating users to submit dangerous operations.
CSRF
What is CSRF
CSRF ( Cross-site request forgery ): the attacker lures the victim into visiting a third-party website, and from that third-party website, sends cross-site requests to the attacked website. By leveraging the login credentials the victim has already obtained on the attacked website, the attacker bypasses the backend user verification and achieves the goal of impersonating the user to perform some operation on the attacked website
Typical Process
The victim logs in to a.com and keeps the login credential ( Cookie )
The attacker lures the victim into visiting b.com
b.com sends a request to a.com: a.com/act=xx. The browser carries a.com's Cookie by default
After receiving the request, a.com verifies the request, confirms it is the victim's credential, and mistakenly believes the request was sent by the victim
a.com executes act=xx in the victim's name
The attack is complete: without the victim's knowledge, the attacker impersonated the victim and made a.com execute an operation defined by the attacker
Characteristics of CSRF
The attack is generally launched from a third-party website, not the attacked website. The attacked website cannot prevent the attack from happening
The attack exploits the victim's login credentials on the attacked website to impersonate the victim and submit operations; rather than directly stealing data
Throughout the process, the attacker cannot obtain the victim's login credentials, only "borrowing" them
Cross-site requests can take various forms: image URLs, hyperlinks, CORS, Form submissions, etc. Some request methods can be directly embedded in third-party forums or articles, making them difficult to trace
Summary:
- CSRF (usually) happens on a third-party domain.
- A CSRF attacker cannot obtain information such as Cookies, only use them.
CSRF Protection Strategies
Overall approach:
- Block access from unknown external domains
- Same-origin detection
- Samesite Cookie
- Require attaching information that only the same domain can obtain when submitting
- CSRF Token
- Double Cookie verification
- Secondary authentication for sensitive operations
Same-Origin Detection
- Referer Header Check: verify whether the
Refererfield in the request header comes from a legitimate domain, blocking cross-origin requests. - Origin Header Verification: check the
Originfield (applicable to AJAX requests) to ensure the request source matches the target site.
Samesite Cookie Attribute
To solve this problem at the source, Google drafted a proposal to improve the HTTP protocol: adding a Samesite attribute to the Set-Cookie response header. It marks this Cookie as a "same-site Cookie". A same-site Cookie can only be used as a first-party Cookie, not as a third-party Cookie. Samesite has two attribute values: Strict and Lax
Samesite=Strict: this is called strict mode, meaning this Cookie can never be used as a third-party Cookie under any circumstances, without exceptionSamesite=Lax: this is called lax mode, which relaxes the restrictions a bit compared to Strict: if the request is of this kind (one that changes the current page or opens a new page) and is also a GET request, then this Cookie can be used as a third-party Cookie
CSRF Token
Principle: the server generates a unique random token ( Token ) for each user session and embeds it in a form or request header. When the server verifies a request, it checks the validity of the Token; illegitimate requests are rejected for lacking the correct Token
The CSRF Token protection strategy consists of three steps:
- Pass the CSRF Token from the server to the browser ( page injection )
- Requests submitted by the page carry this Token
- The server verifies whether the Token is correct
Double Cookie Verification
Double Cookie uses the following process:
- When the user visits a website page, inject a Cookie into the requested domain with a random string as its content (for example,
csrfcookie=v8g9e4ksfhw). - When the front end initiates a request to the back end, take out the Cookie and add it to the URL parameters (e.g.,
https://www.a.com/comment?csrfcookie=v8g9e4ksfhw). - The backend API verifies whether the field in the Cookie matches the field in the URL parameter; if not, it rejects the request.
Secondary Authentication
When performing sensitive operations, use a secondary authentication via password, TOTP, biometric information, etc.
References
- https://tech.meituan.com/2018/09/27/fe-security.html
- https://tech.meituan.com/2018/10/11/fe-security-csrf.html
- https://web.dev/articles/samesite-cookies-explained?hl=zh-cn
Browser Same-Origin Policy
What Is It
Same-origin policy means that a Cookie set by page A cannot be opened by page B, unless the two pages are "same-origin". So-called "same-origin" refers to "three sames": same protocol, same domain name, same port
What Problem Does It Solve?
To ensure the security of user information and prevent malicious websites from stealing data, it is the cornerstone of browser security
Imagine this situation: website A is a bank. After a user logs in, they go on to browse other websites. What would happen if other websites could read website A's Cookie?
Obviously, if the Cookie contains private information (such as the total deposit), that information would leak. Even more frightening, Cookies are often used to save the user's login state; if the user has not logged out, other websites can impersonate the user and do whatever they want. This is because browsers also stipulate that form submissions are not restricted by the same-origin policy.
This shows that the "same-origin policy" is necessary; otherwise, if Cookies could be shared, the Internet would have no security at all
Circumvention Methods
Cookie
A Cookie is a small piece of information written by the server into the browser, which can only be shared by pages of the same origin. However, if two pages share the same first-level domain and only differ in second-level domains, the browser allows sharing Cookies by setting document.domain.
For example, page A is http://w1.example.com/a.html and page B is http://w2.example.com/b.html. As long as the same document.domain is set, the two pages can share Cookies
Note that this method only applies to Cookies and iframe windows; LocalStorage and IndexDB cannot use this method. In addition, when setting a Cookie, the server can also specify the Cookie's domain as the first-level domain, such as .example.com
iframe
If two windows share the same first-level domain and only differ in second-level domains, setting the document.domain property can circumvent the same-origin policy
For websites with completely different origins, there are currently three methods to solve the communication problem between cross-origin windows
Fragment Identifier
The part of a URL after the # symbol, such as #fragment in http://example.com/x.html#fragment. If only the fragment identifier is changed, the page will not refresh
// The parent window can write information into the child window's fragment identifier
const src = `${originURL}#${data}`
document.getElementById('myIFrame').src = src
// The child window gets notified by listening to the hashchange event.
window.onhashchange = checkMessage
function checkMessage() {
const message = window.location.hash
// ...
}
// The child window can also change the parent window's fragment identifier
parent.location.href = `${target}#${hash}`
window.name
Browser windows have a window.name property. The biggest feature of this property is that regardless of whether the pages are same-origin, as long as they are in the same window, if the previous page sets this property, the next page can read it
- Advantage:
window.namehas a large capacity and can hold very long strings; - Disadvantage: you must listen for changes to the child window's
window.nameproperty, which affects page performance.
window.postMessage
To solve this problem, HTML5 introduced a brand new API: the Cross-Document Messaging API;
This API adds a window.postMessage method to the window object, allowing cross-window communication regardless of whether the two windows are same-origin
// The event object of the message event provides the following three properties
// event.source: the window that sent the message
// event.origin: the URL the message was sent to
// event.data: the message content
// The child window references the parent window via the event.source property, then sends a message
window.addEventListener('message', receiveMessage)
function receiveMessage(event) {
event.source.postMessage('Nice to see you!', '*')
}
LocalStorage
With window.postMessage, reading and writing the LocalStorage of other windows also becomes possible
// The parent window sends a message
const win = document.getElementsByTagName('iframe')[0].contentWindow
const obj = { name: 'Jack' }
win.postMessage(
JSON.stringify({ key: 'storage', data: obj }),
'http://bbb.com'
)
// The child window writes the message from the parent window into its own LocalStorage
window.onmessage = function (e) {
if (e.origin !== 'http://bbb.com') {
return
}
const payload = JSON.parse(e.data)
localStorage.setItem(payload.key, JSON.stringify(payload.data))
}
JSONP
- The basic idea is to request JSON data from the server by adding a
<script>element, an approach not restricted by the same-origin policy; after receiving the request, the server sends the data back inside a callback function with a specified name
function addScriptTag(src) {
const script = document.createElement('script')
script.setAttribute('type', 'text/javascript')
script.src = src
document.body.appendChild(script)
}
window.onload = function () {
// This relies on the backend: the responding JS code must explicitly invoke the Callback function
// The Script is executed immediately after loading, which is how complete JSONP is achieved
addScriptTag('http://example.com/ip?callback=foo')
}
function foo(data) {
console.log(`Your public IP address is: ${data.ip}`)
}
WebSocket
WebSocket is a communication protocol that uses ws:// (unencrypted) and wss:// (encrypted) as protocol prefixes. This protocol does not enforce the same-origin policy; as long as the server supports it, cross-origin communication can be carried out through it
Headers of a WebSocket request sent by the browser
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
There is a field called Origin, which indicates the origin of the request, i.e., which domain it comes from. It is precisely because of the Origin field that WebSocket does not enforce the same-origin policy: the server can use this field to decide whether to permit the communication
CORS
- CORS requires support from both the browser and the server. Currently, all browsers support this feature; IE must be no lower than IE10.
- The entire CORS communication process is completed automatically by the browser, requiring no user involvement.
- For developers, CORS communication is no different from same-origin AJAX communication; the code is exactly the same.
- Once the browser detects that an AJAX request is cross-origin, it automatically adds some additional header information, and sometimes an additional request is made, but the user won't notice.
- The key to implementing CORS communication is the server. As long as the server implements the CORS interface, cross-origin communication is possible
Simple Requests and Not-So-Simple Requests
Browsers divide CORS requests into two categories: simple requests and not-so-simple requests; browsers handle these two types of requests differently
A request is a simple request as long as both of the following conditions are met
The request method is one of the following three:
HEAD,GET,POSTThe HTTP headers do not go beyond the following fields
| HTTP headers do not exceed the following fields | Allowed header fields and their content restrictions |
|---|---|
| Accept | Any value, indicating the range of content types the client can accept |
| Accept-Language | Any value, indicating the client's preferred language locale |
| Content-Language | Any value, indicating the language used by the content of the request body |
| Last-Event-ID | Any value, used to identify the event sequence when the server sends an event stream to the client |
| Content-Type | Limited to the following three values: 1. application/x-www-form-urlencoded2. multipart/form-data3. text/plain |
Note: In the table above, "any value" means these fields can carry user-defined content, but it must be within the scope allowed by the CORS cross-origin resource sharing policy.
Basic Flow of a Simple Request
---
title: Simple Request Access Sequence Diagram
---
sequenceDiagram
participant Browser
participant Server
Browser->>Server: GET/POST/HEAD request<br/>carrying the Origin header (e.g. Origin: https://example.com)
Note over Server: Check Origin and configuration
Server-->>Browser: Response data<br/>carrying Access-Control-Allow-Origin: * or a specific origin
Note over Browser: Check whether the response header matches Origin<br/>if it matches, access to the data is allowed
| HTTP Header (response header) | Description |
|---|---|
Access-Control-Allow-Origin |
Required. Its value is either the value of the Origin field in the request, or a *, meaning requests from any domain are accepted. |
Access-Control-Allow-Credentials |
A boolean value indicating whether Cookies are allowed to be sent. By default, Cookies are not included in CORS requests. Setting it to true means the server explicitly permits Cookies to be included in the request and sent to the server together. This value can only be set to true; if the server does not want the browser to send Cookies, simply delete this field |
Access-Control-Expose-Headers |
When requested, the XMLHttpRequest object's getResponseHeader() method can only get 6 basic fields: Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, Pragma. To get other fields, they must be specified in Access-Control-Expose-Headers. |
The withCredentials Property
CORS requests do not send Cookies or HTTP authentication information by default. To send Cookies to the server, on one hand the server must agree by specifying the Access-Control-Allow-Credentials field;
on the other hand, the developer must turn on the withCredentials property in the AJAX request.
Otherwise, even if the server agrees to send Cookies, the browser will not send them. Or, if the server requires setting a Cookie, the browser will not process it
However, if the withCredentials setting is omitted, some browsers will still send Cookies along. In that case, you can explicitly turn off withCredentials
Note that if you want to send Cookies, Access-Control-Allow-Origin cannot be set to an asterisk; it must specify an explicit domain that matches the requesting page
const xhr = new XMLHttpRequest()
xhr.withCredentials = true
Not-So-Simple Request Flow
---
title: Not-So-Simple Request Access Sequence Diagram
---
sequenceDiagram
participant Browser
participant Server
Browser->>Server: OPTIONS preflight request<br/>carrying Origin, Access-Control-Request-Method, Access-Control-Request-Headers
Note over Server: Check Origin, allowed methods and request headers
Server-->>Browser: Preflight response<br/>carrying Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers, etc.
Note over Browser: Verify whether the preflight response passes
Browser->>Server: Send the actual request (e.g. PUT/DELETE)
Server-->>Browser: Respond with actual data<br/>carrying Access-Control-Allow-Origin
Preflight Request
request
The "preflight" request uses the OPTIONS request method, indicating that this request is used to ask. In the header information, the key field is Origin, indicating which origin the request comes from
Access-Control-Request-Method: this field is required and is used to list which HTTP methods the browser's CORS request will useAccess-Control-Request-Headers: this field is a comma-separated string specifying the extra header fields the browser's CORS request will send; in the example above it isX-Custom-Header
OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
response
After receiving the "preflight" request, the server checks the Origin, Access-Control-Request-Method, and Access-Control-Request-Headers fields, confirms that cross-origin requests are allowed, and can then respond
In the HTTP response, the key field is Access-Control-Allow-Origin, indicating that http://api.bob.com can request data. This field can also be set to *, meaning any cross-origin request is accepted
If the server denies the "preflight" request, it returns a normal HTTP response, but without any CORS-related header fields. In that case, the browser determines that the server does not agree to the preflight request, and therefore triggers an error, which is captured by the onerror callback of the XMLHttpRequest object
- Successful response
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
- Failed response
XMLHttpRequest cannot load http://api.alice.com.
Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.
Other CORS-related fields in the server's response are as follows:
Access-Control-Allow-Methods: this field is required; its value is a comma-separated string indicatingall cross-origin request methodssupported by the server. Note that it returns all supported methods, not just the one requested by the browser. This is toavoid multiple "preflight" requestsAccess-Control-Allow-Headers: if the browser request includes theAccess-Control-Request-Headersfield, then theAccess-Control-Allow-Headersfield is required. It is also a comma-separated string, indicating all header fields supported by the server, not limited to the fields requested by the browser in the "preflight"Access-Control-Allow-Credentials: this field has the same meaning as in simple requestsAccess-Control-Max-Age: this field is optional and is used to specify the validity period of this preflight request, in seconds. In the result above, the validity period is 20 days (1,728,000 seconds), meaning this response is allowed to be cached for 1,728,000 seconds (i.e., 20 days), during which no additional preflight request needs to be sent.
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000
Once the server passes the "preflight" request, every subsequent normal CORS request from the browser, just like a simple request, will have an Origin header field. The server's response will also always have an Access-Control-Allow-Origin header field
Summary
Solutions
| Solution | Description |
|---|---|
| Cookie | Cross-origin communication by carrying Cookie information in requests. |
document.domain |
Applicable when the main domains are the same but subdomains differ; cross-origin communication via setting document.domain. |
| iframe fragment identifier | Cross-origin communication by passing information in the URL fragment identifier. |
window.name |
Cross-origin communication using the window.name property. |
window.postMessage |
Cross-origin communication using the window.postMessage method added in HTML5. |
| LocalStorage | Cross-origin communication using the browser's local storage. |
| JSONP | Cross-origin communication by dynamically generating script tags and loading cross-origin resources via the src attribute. |
| WebSocket | Cross-origin communication using the WebSocket protocol, not restricted by the same-origin policy. |
| CORS | Cross-origin requests achieved by setting response headers on the server side to allow specified domains to access resources. |
Key CORS Request Headers
| Request header field | Description |
|---|---|
Origin |
Indicates which origin (protocol + domain + port) the request comes from. |
Access-Control-Request-Method |
Lists which HTTP methods the browser's CORS request will use. |
Access-Control-Request-Headers |
Specifies the extra header fields the browser's CORS request will send. |
Key CORS Response Headers
| Response header field | Description |
|---|---|
Access-Control-Allow-Origin |
Specifies the domains allowed to access the resource |
Access-Control-Allow-Methods |
Specifies all cross-origin request methods supported by the server |
Access-Control-Allow-Headers |
Specifies all header fields supported by the server |
Access-Control-Allow-Credentials |
Specifies whether Cookies are allowed to be sent |
Access-Control-Max-Age |
Specifies the validity period of this preflight request |
Access-Control-Expose-Headers |
Specifies the response header fields the browser can access |
Content Security Policy
What Is CSP?
The essence of Content Security Policy (CSP) is a whitelist system: developers explicitly tell the client which external resources can be loaded and executed, which is equivalent to providing a whitelist. Its implementation and enforcement are entirely done by the browser; developers only need to provide the configuration
What Problem Does CSP Solve?
Cross-site scripting (XSS) is the most common and most harmful web security vulnerability. Preventing it requires many programming measures, which is very troublesome. Many people asked whether the problem could be solved fundamentally, with the browser automatically blocking externally injected malicious scripts. This is the origin of the "Content Security Policy"
How to Enable CSP?
- Via the
Content-Security-Policyfield in the HTTP headers
Content-Security-Policy: script-src 'self'; object-src 'none';style-src cdn.example.org third-party.org; child-src https:
- Via the page's
<meta>tag
<meta
http-equiv="Content-Security-Policy"
content="script-src 'self'; object-src 'none'; style-src cdn.example.org third-party.org; child-src https:"
/>
Explanation of the above configuration
- Scripts: only trust the current domain
<object>tag: trust no URL, i.e., load no resources- Stylesheets: only trust cdn.example.org and third-party.org
- Frames: must be loaded over the HTTPS protocol
- Other resources: no restrictions
How to Configure CSP?
Resource Loading Restriction Configuration
| Directive | Description |
|---|---|
script-src |
External scripts |
style-src |
Stylesheets |
img-src |
Images |
media-src |
Media files (audio and video) |
font-src |
Font files |
object-src |
Plugins (such as Flash) |
child-src |
Frames |
frame-ancestors |
Embedded external resources (such as <frame>, <iframe>, <embed>, <applet>) |
connect-src |
HTTP connections (via XHR, WebSockets, EventSource, etc.) |
worker-src |
Worker scripts |
manifest-src |
Manifest files |
default-src
default-src is used to set the default value for each of the options above
URL Restrictions
A page interacts with other URLs, and this can also be restricted
| Directive | Description |
|---|---|
frame-ancestors |
Restrict the pages embedding the frame |
base-uri |
Restrict <base#href> |
form-action |
Restrict <form#action> |
Other Restrictions
| Directive | Description |
|---|---|
block-all-mixed-content |
HTTPS pages must not load HTTP resources (browsers already enable this by default) |
upgrade-insecure-requests |
Automatically switch all HTTP links that load external resources on the page to the HTTPS protocol |
plugin-types |
Restrict the plugin formats that can be used |
sandbox |
Restrictions on browser behavior, such as disallowing pop-up windows |
report-uri
report-uri is used to tell the browser which URL injection behaviors should be reported to
How to Configure CSP Option Values?
Each restriction option can be set to the following values, which together form the whitelist
| Directive | Description |
|---|---|
| Hostname | example.org, https://example.com:443 |
| Path name | example.org/resources/js/ |
| Wildcard | .example.org, ://.example.com: |
| Protocol name | https:, data: |
Keyword 'self' |
'self' |
Keyword 'none' |
'none' |
Multiple values can also be listed side by side, separated by spaces
Content-Security-Policy: script-src 'self' https://apis.google.com
Special values for script-src
| Directive | Description |
|---|---|
'unsafe-inline' |
Allow executing inline <script> tags and event handler functions in the page |
'unsafe-eval' |
Allow executing strings as code, such as using functions like eval, setTimeout, setInterval, Function, etc. |
nonce value |
Each HTTP response provides an authorization token; an inline script must carry this token to be executed |
hash value |
List the hash values of the script code allowed to execute; an inline script can only execute if its hash value matches. |
Notes
script-srcandobject-srcmust be set unlessdefault-srcis setBecause as long as an attacker can inject a script, all other restrictions can be circumvented. And object-src must be set because external scripts can be executed inside Flash
script-srcmust not use theunsafe-inlinekeyword (unless accompanied by anoncevalue), nor allowdata:URL<!-- Two examples of malicious attacks --> <img src="x" onerror="evil()" /> <script src="data:text/javascript,evil()"></script>Special attention must be paid to JSONP callback functions
<!-- The loaded script comes from the current domain, but by rewriting the callback function, an attacker can still execute malicious code --> <script src="/path/jsonp?callback=alert(document.domain)//"></script>
Extension: Content-Security-Policy-Report-Only
Besides Content-Security-Policy, there is also a Content-Security-Policy-Report-Only field, which means the restriction options are not enforced, only violations of the restrictions are recorded. It must be used together with the report-uri option
Content-Security-Policy-Report-Only: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;
Browser Fingerprinting
What Is It
An online tracking technology that uniquely identifies or distinguishes different network clients by collecting and analyzing specific configuration information of the user's browser. The information used includes but is not limited to the following:
- Browser model and version
- Operating system and version
- Screen resolution
- Font list: can be detected using the CSS3 @font-face rule
- Browser plugin and extension list
- Time zone settings
- System language
- WebGL graphics rendering information
- Some HTTP request header fields: such as UA
- Information exposed by HTML5: such as canvas fingerprints, audio fingerprints, etc.
With this information, even if the user clears cookies or uses incognito mode, it is still difficult to completely avoid being identified and tracked.
Value
In theory, when the user is not logged in, and the browser cannot read or store persistent cookies, the client's IP cannot be read, or different browsers are switched on the same device, we can still fully or partially identify a single device through the browser fingerprint. The main application scenarios are as follows:
- User tracking and identification: for example, on a content distribution website, user A likes to browse ACG content. By recording this interest via the browser fingerprint, the next time the user visits, ACG information can be pushed to user A without requiring a login
- Security and anti-fraud: in fields requiring high security, browser fingerprinting can help enterprises identify potential malicious users or bots, improving system security and preventing fraudulent transactions by fingerprinting access requests.
- Compliance and regulation: in regions with strict legal requirements, browser fingerprints can assist in verifying user identity, ensuring services are only provided to users who meet conditions such as age and region, thereby achieving compliance.
- Data analysis: enterprises and research institutions can collect and analyze browser fingerprint data to understand the behavioral patterns of user groups, device distribution, market trends, and other information.
Development Process
Stateful Tracking
Mainly relies on technologies such as Cookies generated when users log in and evercookie for tracking; this information requires user interaction (such as logging in) to be obtained.
Static Browser Fingerprints
Build a fingerprint by collecting the browser's basic properties, such as the User-Agent string, plugin information, system font list, and other distinguishing feature values. These features are relatively stable and do not change with user sessions or browsing history.
Dynamic Browser Fingerprints
Generate fingerprints using subtle differential data exposed by various HTML5 APIs such as Canvas drawing, WebGL rendering, and audio contexts. These dynamic features can capture deeper information about the browser, including hardware-related characteristics, greatly improving the accuracy of user identification.
Behavioral Fingerprints
Introduce user behavior patterns as part of the fingerprint, such as mouse movement trajectories, page scrolling speed, keystroke intervals, and other non-device-inherent features. Behavioral fingerprints contain not only static and dynamic features, but also combine time-series behavioral data, making tracking more precise and harder to evade.
Collecting Basic Fingerprints
Basic browser fingerprints are a set of attributes used to distinguish different browser environments that do not depend on user interaction or website-stored local data (such as Cookies) and can be collected merely by analyzing the client browser's basic configuration information. Common information and collection methods are shown in the table below:
| Type | Property | How to Obtain |
|---|---|---|
| Browser feature | User-Agent | navigator.userAgent |
| Browser feature | Browser language | navigator.language |
| Browser feature | Plugin list | navigator.plugins |
| System feature | Operating system information | navigator.platform |
| Time zone feature | Difference between local time and Greenwich Mean Time (minutes) | new Date().getTimezoneOffset() |
| Time zone feature | Full time zone information | Requires querying the server |
| Time zone feature | Geographic latitude and longitude | navigator.geolocation.getCurrentPosition |
| Time zone feature | Geographic region name | Requires querying the server |
| Time zone feature | IP address | Requires obtaining via a server interface |
| Hardware feature | Maximum number of simultaneous touch points | navigator.maxTouchPoints |
| Hardware feature | Number of available logical processor cores | navigator.hardwareConcurrency |
| Screen feature | Screen width and height | screen.width and screen.height |
| Screen feature | Combination of screen resolution and color depth | screen.width, screen.height, screen.colorDepth |
- For information that needs to be obtained through the server, client-side JavaScript usually cannot directly obtain the detailed geographic region, IP address, or full time zone information.
- When using
navigator.geolocation.getCurrentPositionto obtain latitude and longitude, the user's explicit authorization is required, and it must be called in a browser that supports this API and runs in a secure context.
Collecting Advanced Fingerprints
Canvas Fingerprint
By calling the Canvas API, drawing graphics, and obtaining the rendered pixel data, subtle differences arise from different browser and device combinations.
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
ctx.textBaseline = 'top'
ctx.font = '14px Arial'
ctx.fillText('Hello, world', 0, 0)
// imageData is a one-dimensional array containing the pixel information rendered by Canvas
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height).data
WebGL Fingerprint
Use the WebGL API to obtain graphics processor-related information; different GPUs produce unique shader compilation results.
const gl = document.createElement('canvas').getContext('webgl')
const fingerprint = {}
fingerprint.vendor = gl.getParameter(gl.VENDOR)
fingerprint.renderer = gl.getParameter(gl.RENDERER)
// More hardware-related parameters can be extracted further
Reference: fingerprint.js source code
Audio Fingerprint
Analyze audio processing capabilities via AudioContext; the audio APIs of different devices may differ.
const audioContext = new (window.AudioContext || window.webkitAudioContext)()
const fingerprint = audioContext.destination.channelCount
Reference: fingerprint.js source code
WebRTC
WebRTC ( Web Real-Time Communication ) is a technology that allows web browsers to communicate in real time without installing plugins or software.
It enables users to exchange audio, video, and data streams directly between browsers, supporting features such as peer-to-peer audio and video calls and screen sharing.
Through the navigator.mediaDevices.getUserMedia() API or the three main WebRTC interfaces APIMediaStream, RTCPeerConnection, RTCDataChannel, some device-specific information can be accessed, for example:
- Local IP address: WebRTC implementations may expose part of the local network configuration, such as LAN IPv4 or IPv6 addresses.
- Hardware codec information: different devices may support different audio and video codecs, and this information can be obtained through WebRTC.
Modern browsers have taken measures to limit WebRTC from leaking too much local device information, such as no longer exposing local IP addresses
if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) {
navigator.mediaDevices.enumerateDevices().then((devices) => {
devices.forEach((device) => {
console.log(
`${device.kind}: ${device.label} id = ${device.deviceId}`
)
// For audio input devices, their specific properties can also be queried
if (device.kind === 'audioinput') {
// It is not recommended to try to obtain sensitive information such as IP in real applications
// This is only an example of displaying device information
// The WebRTC IP leak issue has received attention from modern browsers and is being gradually fixed
}
})
})
}