Skip to content
Snippets Groups Projects
Verified Commit 3c5266fe authored by Frank Sauerburger's avatar Frank Sauerburger
Browse files

Implement working TLS tracker version

parent c9a9731a
No related branches found
No related tags found
No related merge requests found
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Brunch</title>
<link rel="stylesheet" href="/app.css">
<script src="/vendor.js"></script>
<script src="/app.js"></script>
<title>TLS Tracking</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="style.css" />
<script src="vendor.js"></script>
<script src="script.js"></script>
<script>require('initialize');</script>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div class="brunch">
<a href="http://brunch.io"><img src="http://brunch.io/images/logo.png" alt="Brunch"></a>
<p>Bon Appétit.</p>
<div id="app"></div>
</div>
<header>
<h1><a href="/">TLS Tracking</a></h1>
</header>
<main>
<p>
This page demonstrates how TLS and be abused to track users employing HTTP Strict Transport Security (HSTS).
</p>
<div id="react-root"></div>
</main>
<footer>
<p>
&copy; 2019 <a href="mailto:frank@sauerburger.com">Frank Sauerburger</a> &bull;
<a href="imprint.html">Imprint</a> &bull;
<a href="privacy.html">Privacy Policy</a>
</p>
</footer>
</body>
</html>
import React from 'react';
class HostIdenticator extends React.Component {
render() {
var label, className;
if (this.props.https === null) {
className = "unknown";
label = "?";
} else if (this.props.https) {
className = "https";
label = "https";
} else {
className = "http";
label = "http";
}
return <div className={className}>{ label }</div>;
}
}
class IndicatorPanel extends React.Component {
render() {
return (
<div className="indicator-panel">
{this.props.bits.map((bit, i) => {
return (
<HostIdenticator key={`host-ind-${i}`} https={ bit } />
);
})}
</div>
);
}
}
class ContentIndicator extends React.Component {
render() {
var className = this.props.ready ? "ready" : "pending";
var label = this.props.ready ? "Ready ✔" : "loading...";
return (
<div className="content-indicator">
<div className={className}>{this.props.text}</div>
<div className={className}>{label}</div>
</div>
);
}
}
function pad(n, width, z) {
z = z || '0';
n = n + '';
return n.length >= width ? n : new Array(width - n.length + 1).join(z) +
n;
}
var format = n => pad(n, 3);
class ComplexEncoder extends React.Component {
constructor(props) {
super(props);
this.state = {bits: new Array(this.props.maxBit).fill(null), inputValue: ""};
this.encodeInput = this.encodeInput.bind(this);
this.updateInputValue = this.updateInputValue.bind(this);
this.handleKeyPress = this.handleKeyPress.bind(this);
}
is_ready() {
return this.state.bits.every((b) => (b !== null));
}
has_content() {
return this.state.bits.some((b) => (b == true && b !== null));
}
build_callback(index) {
return (res => {
var d = this.state.bits;
if (res.trim() == "http") {
d[index] = false;
} else if (res.trim() == "https") {
d[index] = true;
this.setState({all_false: false});
} else {
d[index] = null;
}
this.setState({bits: d});
for (var i=0; i<this.props.maxBit; i++) {
if (d[i] === null) {
return;
}
}
this.setState({ready: true});
});
}
componentDidMount() {
for (var i=0; i<this.props.maxBit; i++) {
var key = "b" + format(i);
fetch("http://" + key + ".tls-tracking.sauerburger.com")
.then(res => res.text())
.then(this.build_callback(i));
}
}
encode(string) {
var binary = "";
for (var i=0; i<string.length; i++) {
binary += pad(string.charCodeAt(i).toString(2), 8);
}
for (var i=0; i<binary.length && i < this.props.maxBit; i++) {
if (binary[i] == "1") {
var key = "b" + format(i);
fetch("https://" + key + ".tls-tracking.sauerburger.com")
.then(res => res.text())
.then(this.build_callback(i));
}
}
}
decode() {
var decoded = "";
var binary = this.state.bits.map(x => {return x ? "1" : "0"}).join("");
for (var i = 0; i < binary.length / 8; i++) {
var charCode = parseInt(binary.substring(i * 8, (i+1) * 8), 2);
if (charCode == 0) {
decoded += "";
} else {
decoded += String.fromCharCode(charCode);
}
}
return decoded;
}
encodeInput() {
this.encode(this.state.inputValue);
}
updateInputValue(evt) {
this.setState({inputValue: evt.target.value});
}
handleKeyPress(evt) {
if (evt.key === "Enter") {
this.encodeInput();
}
}
render() {
return (<div id="complexEncoder">
<IndicatorPanel bits={this.state.bits} />
{ this.has_content() ?
(<div>Your browser is tracked with the following string:
<ContentIndicator text={this.decode()} ready={this.is_ready()} />
</div>) :
(<div><p>Your browser is not yet tracked. Enter an arbitary phrase
to be encoded. </p>
<p><input type="text" maxLength={this.props.maxBit / 8}
onChange={this.updateInputValue}
onKeyPress={this.handleKeyPress}/>
<input type="button" value="Submit" onClick={this.encodeInput} />
</p></div>)
}
<p> Please note, to remove the tracking tag, you need
to clear the recent history in your browser.</p>
</div>);
}
}
export default class App extends React.Component {
render() {
return (
<div id="content">
<h5>Time to <a href="https://facebook.github.io/react/">React</a>.</h5>
<ComplexEncoder maxBit={128}/>
</div>
);
}
......
......@@ -3,5 +3,5 @@ import React from 'react';
import App from 'components/App';
document.addEventListener('DOMContentLoaded', () => {
ReactDOM.render(<App />, document.querySelector('#app'));
ReactDOM.render(<App />, document.querySelector('#react-root'));
});
.brunch {
font-family: -apple-system, Sans-Serif;
@font-face {
font-family: "Suprema";
src: url("SupremaRegular.woff") format('woff');
}
html {
min-height: 100%;
position: relative;
margin: 0px;
padding: 0px;
background-color: #f2f2f0;
}
body{
margin: 0px;
padding: 0px;
font-family: sans-serif;
}
header h1 {
margin: 0.8em auto 0.4em;
}
header {
font-family: "Suprema", sans-serif;
border-top: 1px solid #0e2f43;
margin: 0px;
background-color: #0e2f43;
border-bottom: 8px solid #2d8891;
color: #f2f2f0;
}
main {
margin: 0px auto 5em;
padding: 0em 1em;
text-align: justify;
}
@media only screen and (min-width: 820px) {
main {
width: 780px;
}
header h1 {
width: 780px;
}
footer p {
width: 780px;
}
}
code {
background-color: #d7bf7866;
font-family: monospace;
padding: 0.2em 0.7em;
}
.panel {
background-color: #cf5b55;
text-align: center;
font-size: 24pt;
color: #3f894a;
padding: 1em;
margin: 1em 6em;
color: #fff;
font-weight: bold;
border-radius: 5px;
}
.started {
/*background-color: #55cf85;*/
background-color: #2d8891;
}
footer {
position: absolute;
bottom: 0px;
width: 100%;
}
footer p {
margin: 4em auto 0em;
padding: 1em;
border-top: 6px solid #2d8891;
text-align: right;
background-color: #0e2f43;
}
a {
color: #3f894a;
color: #2d8891;
text-decoration: none;
border-bottom: 1px solid rgba(0, 0, 0, 0);
}
footer, footer a {
color: #ddd;
}
.terms h2 {
counter-reset: section;
}
a:hover {
color: #27552e;
.terms h3:before {
content: counter(section) ".\0000a0\0000a0";
counter-increment: section;
counter-reset: list;
}
h5 > a {
text-decoration: underline;
.terms ol ol {
list-style-type: lower-alpha;
}
#complexEncoder {
background-color: #fff;
padding: 1em;
margin: 1em;
}
.indicator-panel {
width: 515px;
margin: 1em auto;
}
.indicator-panel div {
width: 24px;
height: 24px;
padding: 2px;
line-height: 24px;
font-size: 10px;
margin: 1px;
text-align: center;
border-radius: 10px;
display: inline-block;
}
.indicator-panel div:nth-child(8n) {
margin-right: 16px;
}
.indicator-panel .unknown {
background-color: #ceaa2c;
}
.indicator-panel .https {
background-color: #46ce2c;
}
.indicator-panel .http {
background-color: #812529;
color: #fff;
}
.content-indicator div {
display: inline-block;
padding: 0.6em;
}
.content-indicator {
margin: 0.4em 2em;
}
.content-indicator div:first-child {
font-family: monospace;
background-color: #ddd;
}
.content-indicator div.ready:first-child {
color: #2d8891;
}
.content-indicator div.ready {
color: #0e2f43;
}
......@@ -3,10 +3,10 @@ exports.files = {
javascripts: {
joinTo: {
'vendor.js': /^(?!app)/,
'app.js': /^app/
'script.js': /^app/
}
},
stylesheets: {joinTo: 'app.css'}
stylesheets: {joinTo: 'style.css'}
};
exports.plugins = {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment