Skip to content
Back to blog

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 Referer field in the request header comes from a legitimate domain, blocking cross-origin requests.
  • Origin Header Verification: check the Origin field (applicable to AJAX requests) to ensure the request source matches the target site.

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 exception
  • Samesite=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:

  1. Pass the CSRF Token from the server to the browser ( page injection )
  2. Requests submitted by the page carry this Token
  3. The server verifies whether the Token is correct

Double Cookie uses the following process:

  1. 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 ).
  2. 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 ).
  3. 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

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

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.name has a large capacity and can hold very long strings;
  • Disadvantage: you must listen for changes to the child window's window.name property, 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

  1. The request method is one of the following three: HEAD, GET, POST

  2. The 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-urlencoded
2. multipart/form-data
3. 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 use
  • Access-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 is X-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 indicating all cross-origin request methods supported by the server. Note that it returns all supported methods, not just the one requested by the browser. This is to avoid multiple "preflight" requests
  • Access-Control-Allow-Headers: if the browser request includes the Access-Control-Request-Headers field, then the Access-Control-Allow-Headers field 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 requests
  • Access-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-Policy field 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-src and object-src must be set unless default-src is set

    Because 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-src must not use the unsafe-inline keyword (unless accompanied by a nonce value), nor allow data: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
  1. 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.
  2. When using navigator.geolocation.getCurrentPosition to 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
      }
    })
  })
}

References