diff --git a/cfg/conf.sample.php b/cfg/conf.sample.php index fba5df8f..01dd32df 100644 --- a/cfg/conf.sample.php +++ b/cfg/conf.sample.php @@ -77,6 +77,12 @@ languageselection = false ; sha256 in HMAC for the deletion token zerobincompatibility = false +; Enable or disable the warning message when the site is served over an insecure connection (insecure HTTP instead of HTTPS), defaults to true. +; Secure transport methods like Tor and I2P domains are automatically whitelisted. +; It is **strongly discouraged** to disable this. +; See https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-it-show-me-an-error-about-an-insecure-connection for more information. +httpwarning = true + [expire] ; expire value that is selected per default ; make sure the value exists in [expire_options] diff --git a/css/bootstrap/privatebin.css b/css/bootstrap/privatebin.css index 8355ac38..1c7bc280 100644 --- a/css/bootstrap/privatebin.css +++ b/css/bootstrap/privatebin.css @@ -81,12 +81,11 @@ body.loading { } .dragAndDropFile{ - color:#777; - font-size:1em; - display:inline; + color:#777; + font-size:1em; + display:inline; } - #deletelink { float: right; margin-left: 5px; diff --git a/css/privatebin.css b/css/privatebin.css index 5dab6c0f..c7124e2a 100644 --- a/css/privatebin.css +++ b/css/privatebin.css @@ -290,9 +290,9 @@ input { #ienotice a { color: #000; } -#oldienotice { display: none; } +#oldnotice, #httpnotice { display: none; } -.errorMessage { +#errormessage, .errorMessage { background-color: #f77 !important; color:#ff0; } diff --git a/i18n/de.json b/i18n/de.json index 7c81cc8e..de62e8fb 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -149,10 +149,17 @@ "Loading…": "Lädt…", "Decrypting paste…": "Entschlüssle Text…", "Preparing new paste…": "Bereite neuen Text vor…", - "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "Wenn diese Nachricht nicht mehr verschwindet, schau bitte in die FAQ (englisch), um zu sehen, wie der Fehler behoben werden kann.", + "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": + "Wenn diese Nachricht nicht mehr verschwindet, schau bitte in die FAQ (englisch), um zu sehen, wie der Fehler behoben werden kann.", "+++ no paste text +++": "+++ kein Paste-Text +++", "Could not get paste data: %s": "Text konnte nicht geladen werden: %s", - "QR code": "QR code" + "QR code": "QR code", + "I love you too, bot…": "I love you too, bot…", + "This website is using an insecure HTTP connection! Please use it only for testing.": + "This website is using an insecure HTTP connection! Please use it only for testing.", + "For more information see this FAQ entry.": + "For more information see this FAQ entry.", + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.": + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS." } diff --git a/i18n/es.json b/i18n/es.json index fc98518b..6dd9e912 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -149,10 +149,17 @@ "Loading…": "Cargando…", "Decrypting paste…": "Descifrando \"paste\"…", "Preparing new paste…": "Preparando \"paste\" nuevo…", - "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "En caso de que este mensaje nunca desaparezca por favor revise este FAQ para obtener información para solucionar problemas.", + "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": + "En caso de que este mensaje nunca desaparezca por favor revise este FAQ para obtener información para solucionar problemas.", "+++ no paste text +++": "+++ \"paste\" sin texto +++", "Could not get paste data: %s": "No se pudieron obtener los datos: %s", - "QR code": "Código QR" -} \ No newline at end of file + "QR code": "Código QR", + "I love you too, bot…": "I love you too, bot…", + "This website is using an insecure HTTP connection! Please use it only for testing.": + "This website is using an insecure HTTP connection! Please use it only for testing.", + "For more information see this FAQ entry.": + "For more information see this FAQ entry.", + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.": + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS." +} diff --git a/i18n/fr.json b/i18n/fr.json index 414a363b..9b0a8728 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -158,10 +158,17 @@ "Loading…": "Chargement…", "Decrypting paste…": "Déchiffrement du paste…", "Preparing new paste…": "Préparation du paste…", - "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "Si ce message ne disparaîssait pas, jetez un oeil à cette FAQ pour des idées de résolution (en Anglais).", + "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": + "Si ce message ne disparaîssait pas, jetez un oeil à cette FAQ pour des idées de résolution (en Anglais).", "+++ no paste text +++": "+++ pas de paste-text +++", "Could not get paste data: %s": "Could not get paste data: %s", - "QR code": "QR code" -} \ No newline at end of file + "QR code": "QR code", + "I love you too, bot…": "I love you too, bot…", + "This website is using an insecure HTTP connection! Please use it only for testing.": + "This website is using an insecure HTTP connection! Please use it only for testing.", + "For more information see this FAQ entry.": + "For more information see this FAQ entry.", + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.": + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS." +} diff --git a/i18n/hu.json b/i18n/hu.json index c12b71b3..da2dd525 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -149,10 +149,17 @@ "Loading…": "Folyamatban...", "Decrypting paste…": "Bejegyzés dekódolása...", "Preparing new paste…": "Új bejegyzés előkészítése...", - "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "Abban az esetben, ha ez az üzenet mindig látható lenne, látogass el a Gyakran Ismételt Kérdések szekcióba a megoldásához.", + "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": + "Abban az esetben, ha ez az üzenet mindig látható lenne, látogass el a Gyakran Ismételt Kérdések szekcióba a megoldásához.", "+++ no paste text +++": "+++ nincs beillesztett szöveg +++", "Could not get paste data: %s": "Could not get paste data: %s", - "QR code": "QR code" -} \ No newline at end of file + "QR code": "QR code", + "I love you too, bot…": "I love you too, bot…", + "This website is using an insecure HTTP connection! Please use it only for testing.": + "This website is using an insecure HTTP connection! Please use it only for testing.", + "For more information see this FAQ entry.": + "For more information see this FAQ entry.", + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.": + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS." +} diff --git a/i18n/it.json b/i18n/it.json index a6123321..125812fa 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -149,10 +149,17 @@ "Loading…": "Carico…", "Decrypting paste…": "Decifro il messaggio…", "Preparing new paste…": "Preparo il nuovo messaggio…", - "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "Nel caso questo messaggio non scompaia, controlla questa FAQ per trovare informazioni su come risolvere il problema (in Inglese).", + "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": + "Nel caso questo messaggio non scompaia, controlla questa FAQ per trovare informazioni su come risolvere il problema (in Inglese).", "+++ no paste text +++": "+++ nessun testo nel messaggio +++", "Could not get paste data: %s": "Could not get paste data: %s", - "QR code": "QR code" -} \ No newline at end of file + "QR code": "QR code", + "I love you too, bot…": "I love you too, bot…", + "This website is using an insecure HTTP connection! Please use it only for testing.": + "This website is using an insecure HTTP connection! Please use it only for testing.", + "For more information see this FAQ entry.": + "For more information see this FAQ entry.", + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.": + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS." +} diff --git a/i18n/nl.json b/i18n/nl.json index c009bcaf..4c93d784 100644 --- a/i18n/nl.json +++ b/i18n/nl.json @@ -149,10 +149,17 @@ "Loading…": "Laden…", "Decrypting paste…": "Geplakte tekst decoderen…", "Preparing new paste…": "Nieuwe geplakte tekst voorbereiden…", - "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "In het geval dat dit bericht nooit verdwijnt, kijkt u dan eens naar veelgestelde vragen voor informatie over het oplossen van problemen .", + "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": + "In het geval dat dit bericht nooit verdwijnt, kijkt u dan eens naar veelgestelde vragen voor informatie over het oplossen van problemen .", "+++ no paste text +++": "+++ geen geplakte tekst +++", "Could not get paste data: %s": "Could not get paste data: %s", - "QR code": "QR code" -} \ No newline at end of file + "QR code": "QR code", + "I love you too, bot…": "I love you too, bot…", + "This website is using an insecure HTTP connection! Please use it only for testing.": + "This website is using an insecure HTTP connection! Please use it only for testing.", + "For more information see this FAQ entry.": + "For more information see this FAQ entry.", + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.": + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS." +} diff --git a/i18n/no.json b/i18n/no.json index 30accacf..108ce8d3 100644 --- a/i18n/no.json +++ b/i18n/no.json @@ -149,10 +149,17 @@ "Loading…": "Laster…", "Decrypting paste…": "Dekrypterer innlegg…", "Preparing new paste…": "Klargjør nytt innlegg…", - "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "Hvis denne meldingen ikke forsvinner kan du ta en titt på siden med ofte stilte spørsmål for informasjon om feilsøking.", + "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": + "Hvis denne meldingen ikke forsvinner kan du ta en titt på siden med ofte stilte spørsmål for informasjon om feilsøking.", "+++ no paste text +++": "+++ ingen innleggstekst +++", "Could not get paste data: %s": "Kunne ikke hente utklippsdata: %s", - "QR code": "QR code" + "QR code": "QR code", + "I love you too, bot…": "I love you too, bot…", + "This website is using an insecure HTTP connection! Please use it only for testing.": + "This website is using an insecure HTTP connection! Please use it only for testing.", + "For more information see this FAQ entry.": + "For more information see this FAQ entry.", + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.": + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS." } diff --git a/i18n/oc.json b/i18n/oc.json index dc4b02cb..b9fac8f8 100644 --- a/i18n/oc.json +++ b/i18n/oc.json @@ -158,10 +158,17 @@ "Loading…": "Cargament…", "Decrypting paste…": "Deschirament del tèxte…", "Preparing new paste…": "Preparacion…", - "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "Se per cas aqueste messatge quita pas de s’afichar mercés de gaitar aquesta FAQ per las solucions (en anglés).", + "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": + "Se per cas aqueste messatge quita pas de s’afichar mercés de gaitar aquesta FAQ per las solucions (en anglés).", "+++ no paste text +++": "+++ cap de tèxte pegat +++", "Could not get paste data: %s": "Recuperacion impossibla de las donadas copiadas : %s", - "QR code": "Còdi QR" + "QR code": "Còdi QR", + "I love you too, bot…": "I love you too, bot…", + "This website is using an insecure HTTP connection! Please use it only for testing.": + "This website is using an insecure HTTP connection! Please use it only for testing.", + "For more information see this FAQ entry.": + "For more information see this FAQ entry.", + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.": + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS." } diff --git a/i18n/pl.json b/i18n/pl.json index 5391bce5..f1ae43f0 100644 --- a/i18n/pl.json +++ b/i18n/pl.json @@ -149,10 +149,17 @@ "Loading…": "Wczytywanie…", "Decrypting paste…": "Odszyfrowywanie wklejki…", "Preparing new paste…": "Przygotowywanie nowej wklejki…", - "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "W przypadku gdy ten komunikat nigdy nie znika, proszę spójrz na to FAQ aby rozwiązać problem (po angielsku).", + "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": + "W przypadku gdy ten komunikat nigdy nie znika, proszę spójrz na to FAQ aby rozwiązać problem (po angielsku).", "+++ no paste text +++": "+++ brak wklejonego tekstu +++", "Could not get paste data: %s": "Nie można było pobrać danych wklejki: %s", - "QR code": "Kod QR" + "QR code": "Kod QR", + "I love you too, bot…": "I love you too, bot…", + "This website is using an insecure HTTP connection! Please use it only for testing.": + "This website is using an insecure HTTP connection! Please use it only for testing.", + "For more information see this FAQ entry.": + "For more information see this FAQ entry.", + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.": + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS." } diff --git a/i18n/pt.json b/i18n/pt.json index 108e743d..f7b7e0bb 100644 --- a/i18n/pt.json +++ b/i18n/pt.json @@ -149,10 +149,17 @@ "Loading…": "Carregando…", "Decrypting paste…": "Decifrando cópia…", "Preparing new paste…": "Preparando nova cópia…", - "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "Caso essa mensagem nunca desapareça, por favor veja este FAQ para saber como resolver os problemas.", + "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": + "Caso essa mensagem nunca desapareça, por favor veja este FAQ para saber como resolver os problemas.", "+++ no paste text +++": "+++ sem texto de cópia +++", "Could not get paste data: %s": "Could not get paste data: %s", - "QR code": "QR code" -} \ No newline at end of file + "QR code": "QR code", + "I love you too, bot…": "I love you too, bot…", + "This website is using an insecure HTTP connection! Please use it only for testing.": + "This website is using an insecure HTTP connection! Please use it only for testing.", + "For more information see this FAQ entry.": + "For more information see this FAQ entry.", + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.": + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS." +} diff --git a/i18n/ru.json b/i18n/ru.json index 9499ed5b..e0840182 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -159,10 +159,17 @@ "Loading…": "Загрузка…", "Decrypting paste…": "Расшифровка записи…", "Preparing new paste…": "Подготовка новой записи…", - "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "Если данное сообщение не исчезает длительное время, посмотрите этот FAQ с информацией о возможном решении проблемы (на английском).", + "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": + "Если данное сообщение не исчезает длительное время, посмотрите этот FAQ с информацией о возможном решении проблемы (на английском).", "+++ no paste text +++": "+++ в записи нет текста +++", "Could not get paste data: %s": "Не удалось получить данные записи: %s", - "QR code": "QR code" + "QR code": "QR code", + "I love you too, bot…": "I love you too, bot…", + "This website is using an insecure HTTP connection! Please use it only for testing.": + "This website is using an insecure HTTP connection! Please use it only for testing.", + "For more information see this FAQ entry.": + "For more information see this FAQ entry.", + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.": + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS." } diff --git a/i18n/sl.json b/i18n/sl.json index 66f9c011..e88961d0 100644 --- a/i18n/sl.json +++ b/i18n/sl.json @@ -158,10 +158,17 @@ "Loading…": "Loading…", "Decrypting paste…": "Decrypting paste…", "Preparing new paste…": "Preparing new paste…", - "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "In case this message never disappears please have a look at this FAQ for information to troubleshoot (in English).", + "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": + "In case this message never disappears please have a look at this FAQ for information to troubleshoot (in English).", "+++ no paste text +++": "+++ no paste text +++", "Could not get paste data: %s": "Could not get paste data: %s", - "QR code": "QR code" + "QR code": "QR code", + "I love you too, bot…": "I love you too, bot…", + "This website is using an insecure HTTP connection! Please use it only for testing.": + "This website is using an insecure HTTP connection! Please use it only for testing.", + "For more information see this FAQ entry.": + "For more information see this FAQ entry.", + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.": + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS." } diff --git a/i18n/zh.json b/i18n/zh.json index 305009c6..2e2c7de9 100644 --- a/i18n/zh.json +++ b/i18n/zh.json @@ -149,10 +149,17 @@ "Loading…": "载入中…", "Decrypting paste…": "正在解密", "Preparing new paste…": "正在准备新的粘贴内容", - "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": - "如果这个消息一直存在,请参考 这里的 FAQ (英文版)进行故障排除。", + "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": + "如果这个消息一直存在,请参考 这里的 FAQ (英文版)进行故障排除。", "+++ no paste text +++": "+++ 没有粘贴内容 +++", "Could not get paste data: %s": "无法获取粘贴数据:%s", - "QR code": "二维码" + "QR code": "二维码", + "I love you too, bot…": "I love you too, bot…", + "This website is using an insecure HTTP connection! Please use it only for testing.": + "This website is using an insecure HTTP connection! Please use it only for testing.", + "For more information see this FAQ entry.": + "For more information see this FAQ entry.", + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.": + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS." } diff --git a/js/common.js b/js/common.js index c8f9b6ba..f593f0c7 100644 --- a/js/common.js +++ b/js/common.js @@ -5,6 +5,7 @@ global.assert = require('assert'); global.jsc = require('jsverify'); global.jsdom = require('jsdom-global'); global.cleanup = global.jsdom(); +global.URL = require('jsdom-url').URL; global.fs = require('fs'); global.WebCrypto = require('node-webcrypto-ossl'); diff --git a/js/package.json b/js/package.json index 7993fbce..38543392 100644 --- a/js/package.json +++ b/js/package.json @@ -10,6 +10,7 @@ "devDependencies": { "jsdom": "^9.12.0", "jsdom-global": "^2.1.1", + "jsdom-url": "^2.2.1", "jsverify": "^0.8.3", "mime-types": "^2.1.20", "node-webcrypto-ossl": "^1.0.37" diff --git a/js/privatebin.js b/js/privatebin.js index 354d0030..51ffbee8 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -176,18 +176,6 @@ jQuery.PrivateBin = (function($, RawDeflate) { const Helper = (function () { const me = {}; - /** - * blacklist of UserAgents (parts) known to belong to a bot - * - * @private - * @enum {Object} - * @readonly - */ - const BadBotUA = [ - 'Bot', - 'bot' - ]; - /** * cache for script location * @@ -365,25 +353,6 @@ jQuery.PrivateBin = (function($, RawDeflate) { return baseUri; }; - - /** - * checks whether this is a bot we dislike - * - * @name Helper.isBadBot - * @function - * @return {bool} - */ - me.isBadBot = function() { - // check whether a bot user agent part can be found in the current - // user agent - for (let i = 0; i < BadBotUA.length; ++i) { - if (navigator.userAgent.indexOf(BadBotUA) >= 0) { - return true; - } - } - return false; - } - /** * wrap an object into a Paste, used for mocking in the unit tests * @@ -861,28 +830,13 @@ jQuery.PrivateBin = (function($, RawDeflate) { */ function getRandomBytes(length) { - if ( - typeof window !== 'undefined' && - typeof Uint8Array !== 'undefined' && - String.fromCodePoint && - ( - typeof window.crypto !== 'undefined' || - typeof window.msCrypto !== 'undefined' - ) - ) { - // modern browser environment - let bytes = ''; - const byteArray = new Uint8Array(length), - crypto = window.crypto || window.msCrypto; - crypto.getRandomValues(byteArray); - for (let i = 0; i < length; ++i) { - bytes += String.fromCharCode(byteArray[i]); - } - return bytes; - } else { - // legacy browser or unsupported environment - throw 'No supported crypto API detected, you may read pastes and comments, but can\'t create pastes or add new comments.'; + let bytes = ''; + const byteArray = new Uint8Array(length); + window.crypto.getRandomValues(byteArray); + for (let i = 0; i < length; ++i) { + bytes += String.fromCharCode(byteArray[i]); } + return bytes; } /** @@ -1227,30 +1181,20 @@ jQuery.PrivateBin = (function($, RawDeflate) { } // do use URL interface, if possible - if (window.URL && window.URL.prototype && ('searchParams' in window.URL.prototype)) { - try { - const url = new URL(window.location); + const url = new URL(window.location); - for (const param of url.searchParams) { - const key = param[0]; - const value = param[1]; + for (const param of url.searchParams) { + const key = param[0]; + const value = param[1]; - if (value === '' && idRegEx.test(key)) { - // safe, as the whole regex is matched - id = key; - return id; - } - } - } catch (e) { - // fallback below - console.error('URL interface not properly supported, error:', e); + if (value === '' && idRegEx.test(key)) { + // safe, as the whole regex is matched + id = key; + return key; } } - // Attention: This also returns the delete token inside of the ID, if it is specified - id = (window.location.search.match(idRegExFind) || [''])[0]; - - if (id === '') { + if (id === null) { throw 'no paste id given'; } @@ -1685,7 +1629,6 @@ jQuery.PrivateBin = (function($, RawDeflate) { */ me.hideMessages = function() { - // also possible: $('.statusmessage').addClass('hidden'); $statusMessage.addClass('hidden'); $errorMessage.addClass('hidden'); }; @@ -4555,6 +4498,163 @@ jQuery.PrivateBin = (function($, RawDeflate) { return me; })(); + + /** + * initial (security) check + * + * @name InitialCheck + * @param {object} window + * @param {object} document + * @class + */ + var InitialCheck = (function () { + var me = {}; + + /** + * blacklist of UserAgents (parts) known to belong to a bot + * + * @private + * @enum {Array} + * @readonly + */ + const badBotUA = [ + 'Bot', + 'bot' + ]; + + /** + * check if the connection is insecure + * + * @private + * @name InitialCheck.isInsecureConnection + * @function + * @return {bool} + */ + function isInsecureConnection() + { + // use .isSecureContext if available + if (window.isSecureContext === true || window.isSecureContext === false) { + return !window.isSecureContext; + } + + const url = new URL(window.location); + + // HTTP is obviously insecure + if (url.protocol !== 'http:') { + return false; + } + + // filter out actually secure connections over HTTP + for (const tld of ['.onion', '.i2p']) { + if (url.hostname.endsWith(tld)) { + return false; + } + } + + // whitelist localhost for development + for (const hostname of ['localhost', '127.0.0.1', '[::1]']) { + if (url.hostname === hostname) { + return false; + } + } + + // totally INSECURE http protocol! + return true; + } + + /** + * checks whether this is a bot we dislike + * + * @private + * @name InitialCheck.isBadBot + * @function + * @return {bool} + */ + function isBadBot() { + // check whether a bot user agent part can be found in the current + // user agent + for (const UAfragment of badBotUA) { + if (navigator.userAgent.indexOf(UAfragment) >= 0) { + return true; + } + } + return false; + } + + /** + * checks whether this is an unsupported browser, via feature detection + * + * @private + * @name InitialCheck.isOldBrowser + * @function + * @return {bool} + */ + function isOldBrowser() { + // webcrypto support + if (typeof window.crypto !== 'object') { + return false; + } + + if (typeof WebAssembly !== 'object' && typeof WebAssembly.instantiate !== 'function') { + return false; + } + try { + // [\0, 'a', 's', 'm', (uint_32) 1] - smallest valid wasm module + const module = new WebAssembly.Module(Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00)); + if ( + !( + module instanceof WebAssembly.Module && + new WebAssembly.Instance(module) instanceof WebAssembly.Instance + ) + ) { + return false; + } + } catch (e) { + return false; + } + + // not checking for async/await, ES6, Promise or Uint8Array support, + // as most browsers introduced these earlier then webassembly and webcrypto: + // https://github.com/PrivateBin/PrivateBin/pull/431#issuecomment-493129359 + + return true; + } + + /** + * init on application start, returns an all-clear signal + * + * @name InitialCheck.init + * @function + * @return {bool} + */ + me.init = function() + { + // prevent bots from viewing a paste and potentially deleting data + // when burn-after-reading is set + if (isBadBot()) { + Alert.showError('I love you too, bot…'); + return false; + } + + if (isOldBrowser()) { + // some browsers (Chrome based ones) would have webcrypto support if using HTTPS + if (isInsecureConnection()) { + Alert.showError(['Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.', 'https' + window.location.href.slice(4)]); + } + $('#oldnotice').removeClass('hidden'); + return false; + } + + if (isInsecureConnection()) { + $('#httpnotice').removeClass('hidden'); + } + + return true; + } + + return me; + })(); + /** * (controller) main PrivateBin logic * @@ -4602,18 +4702,6 @@ jQuery.PrivateBin = (function($, RawDeflate) { Alert.hideLoading(); }; - /** - * shows how we much we love bots that execute JS ;) - * - * @name Controller.showBadBotMessage - * @function - */ - me.showBadBotMessage = function() - { - TopNav.hideAllButtons(); - Alert.showError('I love you too, bot…'); - } - /** * shows the loaded paste * @@ -4741,6 +4829,10 @@ jQuery.PrivateBin = (function($, RawDeflate) { // initialize other modules/"classes" Alert.init(); + if (!InitialCheck.init()) { + // something major is wrong, stop right away + return; + } Model.init(); AttachmentViewer.init(); DiscussionViewer.init(); @@ -4760,19 +4852,12 @@ jQuery.PrivateBin = (function($, RawDeflate) { return me.newPaste(); } - // if delete token is passed (i.e. paste has been deleted by this access) - // there is no more stuf we need to do + // if delete token is passed (i.e. paste has been deleted by this + // access), there is nothing more to do if (Model.hasDeleteToken()) { return; } - // prevent bots from viewing a paste and potentially deleting data - // when burn-after-reading is set - // see https://github.com/elrido/ZeroBin/issues/11 - if (Helper.isBadBot()) { - return me.showBadBotMessage(); - } - // display an existing paste return me.showPaste(); } @@ -4797,6 +4882,7 @@ jQuery.PrivateBin = (function($, RawDeflate) { ServerInteraction: ServerInteraction, PasteEncrypter: PasteEncrypter, PasteDecrypter: PasteDecrypter, + InitialCheck: InitialCheck, Controller: Controller }; })(jQuery, RawDeflate); diff --git a/js/test/Alert.js b/js/test/Alert.js index 617c4889..8f79f7e6 100644 --- a/js/test/Alert.js +++ b/js/test/Alert.js @@ -218,6 +218,7 @@ describe('Alert', function () { return jsc.random(0, 1) ? true : $element; }); functions[trigger](message); + $.PrivateBin.Alert.setCustomHandler(null); return handlerCalled; } ); diff --git a/js/test/CryptTool.js b/js/test/CryptTool.js index 08e54eb9..80ea5ecb 100644 --- a/js/test/CryptTool.js +++ b/js/test/CryptTool.js @@ -10,7 +10,7 @@ describe('CryptTool', function () { this.timeout(30000); it('can en- and decrypt any message', function () { - jsc.check(jsc.forall( + jsc.assert(jsc.forall( 'string', 'string', 'string', @@ -193,7 +193,7 @@ describe('CryptTool', function () { }); it('can en- and decrypt a particular message (#260)', function () { - jsc.check(jsc.forall( + jsc.assert(jsc.forall( 'string', 'string', async function (key, password) { diff --git a/js/test/InitialCheck.js b/js/test/InitialCheck.js new file mode 100644 index 00000000..7931a9ed --- /dev/null +++ b/js/test/InitialCheck.js @@ -0,0 +1,89 @@ +'use strict'; +var common = require('../common'); + +describe('InitialCheck', function () { + describe('init', function () { + this.timeout(30000); + before(function () { + cleanup(); + }); + + it('returns false and shows error, if a bot UA is detected', function () { + jsc.assert(jsc.forall( + 'string', + jsc.elements(['Bot', 'bot']), + 'string', + function (prefix, botBit, suffix) { + const clean = jsdom('', { + 'userAgent': prefix + botBit + suffix + }); + $('body').html( + '' + + '' + ); + $.PrivateBin.Alert.init(); + const result1 = !$.PrivateBin.InitialCheck.init(), + result2 = !$('#errormessage').hasClass('hidden'); + clean(); + return result1 && result2; + } + ), + {tests: 10}); + }); + + jsc.property( + 'shows error, if no webcrypto is detected', + 'bool', + jsc.elements(['localhost', '127.0.0.1', '[::1]', '']), + jsc.nearray(common.jscA2zString()), + jsc.elements(['.onion', '.i2p', '']), + function (secureProtocol, localhost, domain, tld) { + const isDomain = localhost === '', + isSecureContext = secureProtocol || !isDomain || tld.length > 0, + clean = jsdom('', { + 'url': (secureProtocol ? 'https' : 'http' ) + '://' + + (isDomain ? domain.join('') + tld : localhost) + '/' + }); + $('body').html( + ''+ + '' + ); + const crypto = window.crypto; + window.crypto = null; + $.PrivateBin.Alert.init(); + const result1 = !$.PrivateBin.InitialCheck.init(), + result2 = isSecureContext === $('#errormessage').hasClass('hidden'), + result3 = !$('#oldnotice').hasClass('hidden'); + window.crypto = crypto; + clean(); + return result1 && result2 && result3; + } + ); + + jsc.property( + 'shows error, if HTTP only site is detected', + 'bool', + jsc.elements(['localhost', '127.0.0.1', '[::1]', '']), + jsc.nearray(common.jscA2zString()), + jsc.elements(['.onion', '.i2p', '']), + function (secureProtocol, localhost, domain, tld) { + const isDomain = localhost === '', + isSecureContext = secureProtocol || !isDomain || tld.length > 0, + clean = jsdom('', { + 'url': (secureProtocol ? 'https' : 'http' ) + '://' + + (isDomain ? domain.join('') + tld : localhost) + '/' + }); + $('body').html( + ''+ + '' + ); + $.PrivateBin.Alert.init(); + const result1 = $.PrivateBin.InitialCheck.init(), + result2 = isSecureContext === $('#httpnotice').hasClass('hidden'); + clean(); + return result1 && result2; + } + ); + }); +}); + diff --git a/js/test/Model.js b/js/test/Model.js index 57aa22ec..cfcd6dba 100644 --- a/js/test/Model.js +++ b/js/test/Model.js @@ -93,8 +93,9 @@ describe('Model', function () { clean = jsdom('', { url: schema.join('') + '://' + address.join('') + '/?' + queryString + '#' + fragment - }), - result = $.PrivateBin.Model.getPasteId(); + }); + global.URL = require('jsdom-url').URL; + var result = $.PrivateBin.Model.getPasteId(); $.PrivateBin.Model.reset(); clean(); return pasteIdString === result; @@ -111,6 +112,7 @@ describe('Model', function () { '/#' + fragment }), result = false; + global.URL = require('jsdom-url').URL; try { $.PrivateBin.Model.getPasteId(); } diff --git a/js/test/ServerInteraction.js b/js/test/ServerInteraction.js index 64377379..21aa18d1 100644 --- a/js/test/ServerInteraction.js +++ b/js/test/ServerInteraction.js @@ -9,7 +9,7 @@ describe('ServerInteraction', function () { }); this.timeout(30000); it('can prepare an encrypted paste', function () { - jsc.check(jsc.forall( + jsc.assert(jsc.forall( 'string', 'string', 'string', diff --git a/lib/Configuration.php b/lib/Configuration.php index b6d7c419..8202c938 100644 --- a/lib/Configuration.php +++ b/lib/Configuration.php @@ -55,6 +55,7 @@ class Configuration 'icon' => 'identicon', 'cspheader' => 'default-src \'none\'; manifest-src \'self\'; connect-src *; script-src \'self\' \'unsafe-eval\'; style-src \'self\'; font-src \'self\'; img-src \'self\' data: blob:; media-src blob:; object-src blob:; Referrer-Policy: \'no-referrer\'; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals', 'zerobincompatibility' => false, + 'httpwarning' => true, ), 'expire' => array( 'default' => '1week', diff --git a/lib/Controller.php b/lib/Controller.php index 142bb5b7..09b257a9 100644 --- a/lib/Controller.php +++ b/lib/Controller.php @@ -386,6 +386,7 @@ class Controller $page->assign('EXPIREDEFAULT', $this->_conf->getKey('default', 'expire')); $page->assign('URLSHORTENER', $this->_conf->getKey('urlshortener')); $page->assign('QRCODE', $this->_conf->getKey('qrcode')); + $page->assign('HTTPWARNING', $this->_conf->getKey('httpwarning')); $page->draw($this->_conf->getKey('template')); } diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 51255245..c428ceb2 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -72,9 +72,9 @@ if ($MARKDOWN): endif; ?> - - @@ -440,31 +440,42 @@ if ($FILEUPLOAD): -