Archive for the ‘Javascript’ Category

No Comments

Adding HTML from a string the safe way

Friday, April 14th, 2023

Adding HTML from a string isn’t difficult. Something like this works amazingly:

name="John"
html = `<div>Hello <b>${name}</b>,
<p>Welcome on this lovely day</p>
</div>`
document.body.innerHTML=html;

It’s easy and it’s fast, but it is not safe!

Copy and paste above code in your browser console and execute it. It will remove all content, and show the new html.

Let’s assume you get the name from an external resource or user input, instead of John it could be something like `<img src=”z” onerror=”alert(‘Your f*cked’)”`

name=`<img src="z" onerror="alert('Your f*cked')"`
html = `<div>Hello <b>${name}</b>,
<p>Welcome on this lovely day</p>
</div>`
document.body.innerHTML=html;

Copy and paste above code in your browser console and execute it.

It will execute the JavaScript code in the onerror attribute and show an alert. This demo is harmless, but innerHTML is a security hole, that opens the way to severe security risks.

innerHTML is not safe!

But help is on the way, a setHTML function, that can be tweaked by a sanitizer function.

How to use the new and secure way of adding HTML from a string

The syntax for the new function is

setHTML(input, options)

So this will work:

name=`<img src="z" onerror="alert('Your f*cked')"`
html = `<div>Hello <b>${name}</b>,
<p>Welcome on this lovely day</p>
</div>`
document.body.setHTML(html);

Keep in mind innerHTML is a property, and setHTML is an function.

setHTML is working in Chromium and Firefox, although in Firefox it’s behind a config setting. AFAIK it’s still considered experimental. To change it open about:config and toggle the preference to tru

dom.security.setHTML.enabled

Can’t wait until this is shipped as stable.

Somehow the documentation is wrong, it’s not the dom.security.sanitizer.enabled preference , but the `dom.security.setHTML.enabled` that should bet toggled.

The sanitizer preference is for the new Sanitizer() object that can be passed as an option

const attribute_sanitizer =new Sanitizer ( {allowAttributes: {"style": ["div"]}})
document.body.setHTML(unsanitized_string,{sanitizer: attribute_sanitizer}) 

That way you can tweak the elements that you want to be sanitized. Like dropping specified elements, attributes, or comments. Also very useful.

But the default is working fine in Firefox.

No Comments

Adding events to elements the simple way, using optional chaining

Thursday, August 11th, 2022

On modern JavaScript driven or enhanced sites the HTML DOM is sprinkled with events.

Since the birth of HTML5, (and decline of the worst (most expensive) browser ever: Microsoft Explorer) the appropriate way of attaching events is using AddEventListener()

document.querySelector('selector').addEventListener('click', function () { console.log("Hi, you clicked") });

This works only if the queried element does exists. Otherwise you”ll get an error and further execution of the script will fail.

So you’ll need to add a conditional check, the querySelector function returns null when the element can’t be found:

if (document.querySelector('selector'))
Ā document.querySelector('selector').addEventListener('click', function () {
console.log("Hi, you clicked")
});

Modern JavaScript is developing

But functionality to JavaScript is added every year and now there is a optional chaining and that feature is exactly what we need.

Optional chaining was introduced in ES2020. It’s supported by all modern updated browsers.

Optional chaining

Simply add a ? to a object property or method to check if it is existing.

book.author?.name

This will not cause an error if book.author is nullish (not existing)

Using this syntax and arrow functions the new code for attaching an event to an element is:

document.querySelector('selector')?.addEventListener('click', ()=>console.log("Hi, you clicked"));

If the element doesn’t exist, it will not do anything (document.querySelector('selector') is nullish). It won’t cause an error.

Exactly what we need!

UPDATE

What I miss though is something like this:

document.querySelector("#menu-comments a")?.href += "?comment_status=moderated";

Above oneliner is my simple solution to set the default Comments-link in WordPress to show the pending or new comments by default. Most (99%) are SPAM unfortunately, so this way it’s safer to select all and do a bulk action delete permanently or mark as spam.

But above onliner gives an JS error:

Uncaught SyntaxError: invalid assignment left-hand side

To my surprise there was a proposal in the spec to allow this. I would welcome that change, hopefully it will come one day.

I hate writing this verbose conditional:

if (document.querySelector("#menu-comments a"))
document.querySelector("#menu-comments a").href += "?comment_status=moderated";

Maybe I should start writing CoffeeScript or use Babel by default. šŸ˜‰

No Comments

Setting several properties of dataset of any DOM element, writing to the dataset property

Tuesday, June 21st, 2022

One of the greatest additions in writing JS of the last years for writing clean code are arrow functions, classes and several ways of Destructuring assingment.

Arrow functions let you skip, several verbose terms like the function keyword, return keyword and {} character, while maintaining readability. Easy, once you mastered the new syntax!.

What is Destructuring assingment?

Let’s assume you fetch a stock json:

let json= {name:"SHELL PLC",isin:"GB00BP6MXD84",price:"25.00","volume":"2500000",time:"2022-06-22:09-06"}

They old way of dealing with this was to write everything out to assign values to variables:

var name = json.name;
var isin= json.isin
var price = json.price;
var volume=json.volume;
var time = json.time;

Destructuring assingment let you do this more succinct:

let {name,isin,price,volume,time}=json

That is nice code.

Destructuring arrays, or csv rows:

let {name,isin,price,volume,time}=row.split(",")

Sure there is more on parsing cvs rows than just splitting on “,” but this is an example and you get the drift. When you totally control the csv file, using “,” only as separator, it should do the job.

Writing to the datatset property?

I dearly miss something for setting the dataset property of an element concisely.

The dataset read-only property of the HTMLElement interface provides read/write access to custom data attributes (data-*) on elements.

Note: The dataset property itself can be read, but not directly written. Instead, all writes must be to the individual properties within the dataset, which in turn represent the data attributes.

So you can’t simply do something like this:

document.querySelector("#stock").dataset=json

You have to write it all out, and that isĀ  verbose:


let elStock = document.querySelector("#stock")
elStock.dataset.name=json.name;
elStock.isin=json.isin
elStock.price=json.price

Well there is a workaround.

Imitate writing to the dataset property directly, Object.assign()

According to the specs you can’t directly write the dataset, but you can do this:

Object.assign(document.querySelector("#stock"),json)

Result:

<body data-name="SHELL PLC" data-isin="GB00BP6MXD84" data-price="25.00" data-volume="2500000" data-time="2022-06-22:09-06">

A piece of cake. That is as easy as writing the dataset.property directly.

To extract dataset into variables destructuring assignment does work.

let {name,isin,price}=document.body.dataset

I used `Object.assign` in an older project for writing several properties to the style attribute, but it works as well for the dataset property.

No Comments

Security by design: following the right principle

Friday, April 6th, 2018

Since there was some attention to a CSS driven keylogger, it’s good again to point out the security risks of third party content. That risk is huge.

When a third party CSS Stylesheet can steal your password, it would be a piece of cake for any Javascript script to do the same.

If you follow this blog, it will not surprise you that we’re skeptical to all the JavaScript driven frameworks that are fashionable. In general Javascript is bad for security, privacy, and the planet šŸ™‚ : battery usage. It’s good for tracking, fingerprinting and advertisements, and visual eye-candy.

Yes, JavaScript can be nice, but please adhere to these old principles: graceful degradation and progressive enhancement. And it’s fashionable not to follow those principles.

Don’t force JavaScript, because you’re trying to sell adds, or track users.

Don’t overdo JavaScript: an URL with your contact info should have your contact info (in HTML). Otherwise you should point to another URL. Don’t hide this information behind Javascript and in JSON somewhere. You’re breaking the internet. Keep information accessible.

It’s important to underline again. Every script that is loaded with <script> has access to the DOM, and if the page is a login page, it has access to the password and username.

So a simple principle can be deducted, and it’s a shame that we have to repeat it here.

Never add any (third party) script to a login page. NEVER.

Nobody should be authorized to have access to a plain password, except for the user. Nobody. Limit access by design, not by trust.

(more…)

1 Comment

Breaking the bad, pushing a worse internet (part II)

Sunday, June 21st, 2015

In an earlier post we lamented the behavior of multinationals by dropping noble classic Internet principles like Graceful degradation and progressive enhancement to strengthen their business model at a high security and privacy cost for users.

Go to a site with JS disabled and you see Nada. Zilch. On Google, on Twitter, FacebookĀ tells us it can do much without JS. Nonsense, that is their policy, it’s not your fault.

(more…)

No Comments

Fixing Tracking Contact Form 7 with Google Analytics in WordPress

Wednesday, October 1st, 2014

Contact Form 7 advices to add this code to the Additional Settings field at the bottom of the contact form management page

on_sent_ok: "_gaq.push(['_trackEvent', 'Contact Form', 'Submit']);"

Actually that is a bad idea. Be tracked by Google is not every one’s favourite idea of a free internet, so people block Google Analytics either by any tracker blocker, like Ghostery or Disconnect, by Googles official `opt out extension` or by simple blocking the script in a firewall.

Yes, Internet is the only one place on earth you have to `opt-out` to live quiet and peaceful.

When a user has blocked the Analytics script and visits your contact form, he can’t submit it. It will not submit nor show any error-message. It will do nothing, except show an obscure JS error in the console.

`Uncaught exception: ReferenceError: Undefined variable: _gaq`

To fix this, wrap your code up in a try and catch, so it won’t stop on the error and submitting will not halt:

on_sent_ok: "try{_gaq.push(['_trackEvent', 'Contact Form', 'Submit']);} catch(e){}"

Integrating Contact Form 7 and Google Universal Analytics this way is more robust.