Browsers vs. Data and JavaScript URLs
Data and JavaScript URLs come in handy when creating proofs of concept, particularly if you’re working on a test with limited tooling.
By storing data directly in URL bars and as text within the DOM, they can help you turn open redirects into reflected XSS and create CSRF proofs of concept — without even using a webserver!
This page is meant to be interactive, and will run scripts from the DOM.
It’s best read with the element inspector open, or while looking at the page source.
1- What is a Data URL?
http:// is only one of many URL schemes. Browsers use these handlers to do things like fetch files or create links.
Some other common schemes include: mailTo , ftp , smb , or file
For example, if you click on this link, your computer may open an email client such as Outlook and begin a message to
MailToEmail@example.com with the subject line «MailTo»
This link was created using the following code:
<a href=»mailto:MailToEmail@example.com?subject=MailTo»>click on this link</a>
Most handlers tell the browser to obtain data from source separate from the page itself.
For example, an HTML page index.html may include an image tag that points to a file in the same directory, like so:
The browser will interpret this as an instruction to fetch the file from images/picture.jpg and display it on index.html
However, this isn’t the only way of including external resources. An alternative would be to use the data scheme and include the image’s data on index.html itself.
A data URL allows us to define a file on the fly entirely within the context of the currently loaded page — no links to an external file or additional requests!
Consider this example image tag:
While it does not include a link to any location besides this page, the image’s data has been encoded onto the page itself where it can be decoded by the browser and displayed here:
http or file resource.
This file has been created entirely by the information contained in the URL itself. You can do this for all files, not just images.
- data:text/plain,hello
- data:,hello
- data:text;base64,dGhpcyB0ZXh0IHdhcyBiNjQgZW5jb2RlZA==
- data:text/html,Click and type to use this page as a notepad!<html contenteditable>
Entire webpages, including forms and all can be contained within data URLs.
There’s also the javascript URL Scheme
This handler is kind of like a cross between the data URL scheme and entering code into your browser console.
To see it in action and get an alert window popup, open a new tab and paste in the following:
Did something strange happen? Did your browser decide not to paste the javascript: part of the text?
That happened to me, so try typing it in manually to get the popup.
If you’ve been interacting with the examples in this piece so far, you may have noticed that I haven’t put in any links to these URLs.
Instead, I’ve been asking you to paste or type into your URL bar manually.
That’s because you can use these schemes in attacks such as Cross-Site scripting, and browsers contain measures to try and prevent that.
2 — Browsers Don’t Like Top-level Navigation to Data URLs That Much.
. such as in <a> tags.
If you try to navigate to a data URL without entering it into your URL bar directly, the browser may stop you.
Click to try opening a tab to https://example.com using an <a> tag.
This worked for me in both Firefox and Chrome. (when I ran the code from a local HTML file.)
Click to try opening this Data URL in a new tab:
data:text/plain,hello
This worked for me in Firefox, but Chrome began to give error messages on the console. (when I ran the code from a local HTML file.)
Click to try opening this Data URL in a new tab:
data:text/html,<b>hello!</b>
Your browser will most likely prevent you from doing this, giving an error like: Not allowed to navigate top frame to data URL. (I saw this even when I ran the code from a local HTML file.
Click to try opening this Data URL in a new tab:
data:text/html,<svg/onload=alert("hello!")> This didn’t work for me in either browser. (when I ran the code from a local HTML file.)
However, you can paste those data URLs directly into your URL bar and they will load fully.
. or JavaScript
The browsers also don’t like redirecting to data URLs via JavaScript.
Click to try to redirect to https://example.com using the JavaScript:
«window.location=’https://example.com'»
This redirected me in both Chrome and Firefox, even when I opened the page in an iframe.
Click to try to redirect to to this URL
data:text/plain,hello
using the JavaScript:
«window.location=’data:text/plain,hello'»
When run in a frame, this worked in both Firefox and Chrome, as it didn’t try to change the top level window.
When not run from a frame and serving as the top window, this didn’t work in either in Firefox or Chrome.
Click to try to redirect to to this URL
data:text/plain,hello
using the JavaScript:
«top.location.href=’data:text/plain,hello'»
This didn’t work from either a frame or a local file in either Firefox or Chrome.
Click here to try to redirect to this URL:
data:text/html,<b>hello!</b>
using the JavaScript:
«window.location=’data:text/html,%3csvg/onload%3dalert(%22hello!%22)%3e’)»
Again, clicking these buttons didn’t redirect me in either Firefox or Chrome, but pasting these strings into the browser console did.
So, if these measures are in place, can these schemes be used in XSS or not?
3 — Browsers aren’t as averse to javascript: URLs
Click to try opening the JavaScript URL javascript:alert(‘href’)» on this page, no redirection or new tabs.
<a href=»javascript:alert(‘href’)»> Click to try opening</a>
This worked in both Firefox and Chrome, and produced an alert window.
Click to try opening the JavaScript URL javascript:alert(‘href-blank’)» with a target=_blank attribute.
<a href=»javascript:alert(‘href-blank’) target=_blank»> Click to try opening</a>
If you click the link using the middle mouse button, CTRL+left mouse button,
SHIFT+left mouse button or ALT+left mouse button, it will open in a new tab or window.
This worked in both Firefox and Chrome, and produced an alert window.
Normally, with target=_blank , browsers may give an error saying about:blank#blocked
Thanks to Mika Kulmala for this tip!
Click to try opening the JavaScript URL javascript:alert(«location») on this page using
«window.location=’javascript:alert("location")'»
This also worked in Firefox and Chrome, and produced another alert.
There’s potential for XSS here, as it’s a seemingly a loophole that allows behavior the browser may normally forbid.
It’s even possible to chain together data/javascript URLs to make changes to the DOM without additional HTTP requests.
Where PG1hcnF1ZWU+VGhpcyB3YXMgYWRkZWQgd2l0aCBhIDxjb2RlPmphdmFzY3JpcHQ8L2NvZGU+IFVSTDwvbWFycXVlZT4=
decodes to <marquee>This was added with a <code> javascript</code> URL</marquee>
Use data and javascript URLs for easily assembled proofs of concept.
In attacks (or demonstrations) that can normally require resources to be hosted externally (such as on a webserver), it may be possible to drop in a data or javascript URL as an alternative.
Here are some a real scenarios doing just that.
4 — Real-World Examples
A Lightweight Way to Produce Reflected XSS from an Open Redirect
I performed a test of an application that had a few static pages for things like the application’s FAQ, Privacy Policy, and Terms of Use.
On these pages was a back button that was controlled (oddly), by a URL parameter:
https://exampleSite.tld/content?return=[URL GOES HERE]
When the user browsed the site normally, the return parameter would be auto-populated to the relative address of the previous page the user was viewing. The application did not sufficiently filter the value of return before integrating it into the page, and placed it into the href attribute of an <a> tag.
<a href=»URL GOES HERE»>BACK</a>
The application’s XSS sanitization was good enough to limit what was ultimately placed into that tag to an actual link. It prevented any attempts to break out of the <a> tag to achieve less restricted XSS, so the vulnerability here was an Open Redirect that required user interaction.
At this point, an attacker could craft a URL that caused the page to take a user to their malicious server if they clicked the «BACK» link on the page. This attack would have a significant impact, but requires some setup. While this would not necessarily be prohibitive, an attacker may have to navigate around web firewalls that stop users from browsing to sketchy external resources or overcome other obstacles associated with hosting a webserver.
If this vulnerability was found during a test performed from within a client’s test environment, a tester might not have the permissions to host a webserver from their test machine or navigate firewalls. It’s me, I was «a tester.»
To get around this, I used a JavaScript URL to create an XSS payload to demonstrate a visual impact without the need to set up an external server.
javascript:alert(‘Arbitrary Resource’)
This produced the full URL of https://exampleSite.tld/content?return=javascript:alert(‘Arbitrary Resource’)
On the page, this created the following <a> tag, though in reality the single quotes inside the parentheses were HTML encoded:
When the user clicked the button, the JavaScript payload executed. While an alert window barely scratches
the surface of the potential impacts of XSS, it does the trick for a proof of concept in this redacted example.
Using XSS as an impact for the Open Redirect also communicated the need for defense in depth. Even if the environment
were to be locked down in such a way that the user could only access a short allow-list of resources, a data or javascript URL can leverage the Open Redirect to direct them to something malicious without additional HTTP traffic.
In my research on this topic, I came across this article by Robin Wood that talks about some occasions where JavaScript URLs
might not work as expected. It also, importantly, includes information on how to fix that by appending void(0); to the payload. Thanks, Robin!
Data URLs for Quick Sharing of HTML Pages (and maybe social engineering)
Suppose you have a CSRF proof of concept you’re sharing (securely) with another tester or a client. It could take the form of an auto-submitting form page, which you can share as an HTML file. Most of the time, that works fine.
However, recently while working in a bug bounty response role, I needed to create a ticket for Reflected XSS via POST request. The team that needed the proof of concept documented wanted one- step demonstrations — run this curl command, visit this URL, etc.
So, to give them a quick demonstration, I used a data URL that contained an entire CSRF proof of concept that submitted a POST request on page load. This was more expedient than telling them to download a file and open it in their browser, as they could just paste it into the URL bar.
This convenient method isn’t just limited to legitimate usage, but using for it social engineering has its drawbacks. Users would have no reason to be accustomed to pasting strange strings into their URL bars. It’s certainly more out of the ordinary than instead of simply clicking a link that starts with https, and may raise some red flags.
Limitations
data and javascript URLs do have null Origin headers, however.
This can mean that requests that browsers send after processing them may not play well with the Same-Origin-Policy.
The Content-Security-Policy header may also interfere with their operation.
The biggest advantage of data and javascript URLs is portability. While they have limitations, their use can increase a vulnerability’s risk rating in rare cases.
Data URLs
Data URLs, URLs prefixed with the data: scheme, allow content creators to embed small files inline in documents. They were formerly known as «data URIs» until that name was retired by the WHATWG.
Note: Data URLs are treated as unique opaque origins by modern browsers, rather than inheriting the origin of the settings object responsible for the navigation.
Syntax
Data URLs are composed of four parts: a prefix ( data: ), a MIME type indicating the type of data, an optional base64 token if non-textual, and the data itself:
The mediatype is a MIME type string, such as ‘image/jpeg’ for a JPEG image file. If omitted, defaults to text/plain;charset=US-ASCII
If the data contains characters defined in RFC 3986 as reserved characters, or contains space characters, newline characters, or other non-printing characters, those characters must be URL encoded (aka «URL encoded»).
If the data is textual, you can embed the text (using the appropriate entities or escapes based on the enclosing document’s type). Otherwise, you can specify base64 to embed base64-encoded binary data. You can find more info on MIME types here and here.
The text/plain data Hello, World! . Note how the comma is URl encoded as %2C , and the space character as %20 .
base64-encoded version of the above
An HTML document with <h1>Hello, World!</h1>
An HTML document with <script>alert(‘hi’);</script> that executes a JavaScript alert. Note that the closing script tag is required.
Encoding data into base64 format
Base64 is a group of binary-to-text encoding schemes that represent binary data in an ASCII string format by translating it into a radix-64 representation. By consisting only of ASCII characters, base64 strings are generally url-safe, and that’s why they can be used to encode data in Data URLs.
Encoding in JavaScript
The Web APIs have native methods to encode or decode to base64: Base64.
Encoding on a Unix system
Base64 encoding of a file or string on Linux and macOS systems can be achieved using the command-line base64 (or, as an alternative, the uuencode utility with -m argument).
Encoding on Microsoft Windows
On Windows, Convert.ToBase64String from PowerShell can be used to perform the Base64 encoding:
Alternatively, a GNU/Linux shell (such as WSL) provides the utility base64 :
Common problems
This section describes problems that commonly occur when creating and using data URLs.
This represents an HTML resource whose contents are:
The format for data URLs is very simple, but it’s easy to forget to put a comma before the «data» segment, or to incorrectly encode the data into base64 format.
Formatting in HTML
A data URL provides a file within a file, which can potentially be very wide relative to the width of the enclosing document. As a URL, the data should be formattable with whitespace (linefeed, tab, or spaces), but there are practical issues that arise when using base64 encoding.
Browsers are not required to support any particular maximum length of data. For example, the Opera 11 browser limited URLs to 65535 characters long which limits data URLs to 65529 characters (65529 characters being the length of the encoded data, not the source, if you use the plain data: , without specifying a MIME type). Firefox version 97 and newer supports data URLs of up to 32MB (before 97 the limit was close to 256MB). Chromium objects to URLs over 512MB, and Webkit (Safari) to URLs over 2048MB.
Lack of error handling
Invalid parameters in media, or typos when specifying ‘base64’ , are ignored, but no error is provided.
No support for query strings, etc.
The data portion of a data URL is opaque, so an attempt to use a query string (page-specific parameters, with the syntax <url>?parameter-data ) with a data URL will just include the query string in the data the URL represents.
A number of security issues (for example, phishing) have been associated with data URLs, and navigating to them in the browser’s top level. To mitigate such issues, top-level navigation to data: URLs is blocked in all modern browsers. See this blog post from the Mozilla Security Team for more details.
Data URLs
Data URLs, URLs prefixed with the data: scheme, allow content creators to embed small files inline in documents. They were formerly known as «data URIs» until that name was retired by the WHATWG.
Note: Data URLs are treated as unique opaque origins by modern browsers, rather than inheriting the origin of the settings object responsible for the navigation.
Syntax
Data URLs are composed of four parts: a prefix ( data: ), a MIME type indicating the type of data, an optional base64 token if non-textual, and the data itself:
The mediatype is a MIME type string, such as ‘image/jpeg’ for a JPEG image file. If omitted, defaults to text/plain;charset=US-ASCII
If the data contains characters defined in RFC 3986 as reserved characters, or contains space characters, newline characters, or other non-printing characters, those characters must be percent-encoded (aka “URL-encoded”).
If the data is textual, you can embed the text (using the appropriate entities or escapes based on the enclosing document’s type). Otherwise, you can specify base64 to embed base64-encoded binary data. You can find more info on MIME types here and here.
The text/plain data Hello, World! . Note how the comma is percent-encoded as %2C , and the space character as %20 .
base64-encoded version of the above
An HTML document with <h1>Hello, World!</h1>
An HTML document that executes a JavaScript alert. Note that the closing script tag is required.
Encoding data into base64 format
Base64 is a group of binary-to-text encoding schemes that represent binary data in an ASCII string format by translating it into a radix-64 representation. By consisting only of ASCII characters, base64 strings are generally url-safe, and that’s why they can be used to encode data in Data URLs.
Encoding in Javascript
The Web APIs have native methods to encode or decode to base64: Base64 encoding and decoding.
Encoding on a Unix system
Base64 encoding of a file or string on Linux and Mac OS X systems can be achieved using the command-line base64 (or, as an alternative, the uuencode utility with -m argument).
Encoding on Microsoft Windows
On Windows, Convert.ToBase64String from PowerShell can be used to perform the Base64 encoding:
Alternatively, a GNU/Linux shell (such as WSL) provides the utility base64 :
Common problems
This section describes problems that commonly occur when creating and using data URLs.
This represents an HTML resource whose contents are:
The format for data URLs is very simple, but it’s easy to forget to put a comma before the «data» segment, or to incorrectly encode the data into base64 format.
Formatting in HTML
A data URL provides a file within a file, which can potentially be very wide relative to the width of the enclosing document. As a URL, the data should be formattable with whitespace (linefeed, tab, or spaces), but there are practical issues that arise when using base64 encoding.
Although Firefox supports data URLs of essentially unlimited length, browsers are not required to support any particular maximum length of data. For example, the Opera 11 browser limited URLs to 65535 characters long which limits data URLs to 65529 characters (65529 characters being the length of the encoded data, not the source, if you use the plain data: , without specifying a MIME type).
Lack of error handling
Invalid parameters in media, or typos when specifying ‘base64’ , are ignored, but no error is provided.
No support for query strings, etc.
The data portion of a data URL is opaque, so an attempt to use a query string (page-specific parameters, with the syntax <url>?parameter-data ) with a data URL will just include the query string in the data the URL represents.
A number of security issues (for example, phishing) have been associated with data URLs, and navigating to them in the browser’s top level. To mitigate such issues, top-level navigation to data:// URLs has been blocked in Firefox 59+ (release version, Nightly/Beta from 58), and we hope to see other browsers follow suit soon. See Blocking Top-Level Navigations to data URLs for Firefox 58 for more details.
Data URLs
URL- адреса данных, URL- адреса с префиксом data: scheme, позволяют создателям контента встраивать в документы небольшие файлы. Раньше они назывались «URI данных», пока WHATWG не удалила это имя.
Примечание. URL-адреса данных обрабатываются современными браузерами как уникальные непрозрачные источники, а не наследуют источник объекта настроек, отвечающего за навигацию.
Syntax
URL — адрес данных состоят из четырех частей: префикс ( data: ), A типа MIME , указывающий типа данных, дополнительный base64 маркер , если нетекстовые, а сам данные:
mediatype является тип MIME строки, такие , как ‘image/jpeg’ для файла изображения JPEG. Если не указано, по умолчанию используется text/plain;charset=US-ASCII .
Если данные содержат символы, определенные в RFC 3986 как зарезервированные символы , или содержат пробелы, символы новой строки или другие непечатаемые символы, эти символы должны быть закодированы в процентах ( т . н . «URL-кодированные»).
Если данные являются текстовыми, вы можете встроить текст (используя соответствующие сущности или экранирования в зависимости от типа включающего документа). В противном случае вы можете указать base64 для внедрения двоичных данных в кодировке base64. Вы можете найти дополнительную информацию о типах MIME здесь и здесь .
Текст / простые данные Hello, World! . Обратите внимание, как запятая закодирована в процентах как %2C , а символ пробела как %20 .
базовая64-кодированная версия вышеизложенного
HTML-документ с <h1>Hello, World!</h1>
HTML-документ,выполняющий предупреждение JavaScript.Обратите внимание,что требуется закрывающий тег скрипта.
Кодирование данных в формат base64
Base64-это группа схем кодирования двоичных данных в текст,которые представляют двоичные данные в формате строк ASCII путем перевода их в представление radix-64.Состоящие только из символов ASCII,строки base64,как правило,безопасны для url,поэтому их можно использовать для кодирования данных в URL-адресах данных.
Кодирование в JavaScript
У веб-API есть собственные методы для кодирования или декодирования в base64: кодирование и декодирование Base64 .
Кодирование в системе Unix
Кодирование файла или строки в формате Base64 в системах Linux и macOS можно выполнить с помощью командной строки base64 (или, в качестве альтернативы, с помощью утилиты uuencode с аргументом -m ).
Это представляет собой HTML ресурс,содержимое которого:
Формат URL-адресов data очень прост, но легко забыть поставить запятую перед сегментом «данные» или неправильно закодировать данные в формате base64.
Форматирование в HTML
data URL — адрес содержит файл в файл, который потенциально может быть очень широким по сравнению с шириной вмещающего документа. В качестве URL-адреса data должны быть отформатированы с использованием пробелов (перевод строки, табуляция или пробелы), но при использовании кодировки base64 возникают практические проблемы .
Браузеры не обязаны поддерживать какую-либо конкретную максимальную длину данных. Например, браузер Opera 11 ограничивал длину URL-адресов до 65535 символов, что ограничивает URL-адреса data до 65529 символов (65529 символов — это длина закодированных данных, а не источника, если вы используете простые data: без указания типа MIME). Firefox версии 97 и новее поддерживает URL-адреса data размером до 32 МБ (до версии 97 ограничение было близко к 256 МБ). Chromium возражает против URL-адресов размером более 512 МБ, а Webkit (Safari) — для URL-адресов размером более 2048 МБ.
Отсутствие обработки ошибок
Недействительные параметры в носителе или опечатки при указании ‘base64’ игнорируются, но ошибки не выводятся.
Отсутствие поддержки строк запросов и т.д.
Часть данных URL-адреса данных непрозрачна, поэтому попытка использовать строку запроса (параметры страницы, с синтаксисом <url>?parameter-data ) с URL-адресом данных просто включит строку запроса в данные URL-адреса. представляет.
Ряд проблем безопасности (например, фишинг) был связан с URL-адресами данных и переходом к ним на верхнем уровне браузера. Чтобы смягчить такие проблемы, навигация верхнего уровня к data: URL-адреса заблокированы во всех современных браузерах. См. этот пост в блоге команды безопасности Mozilla для получения более подробной информации.