Javascript to prevent clickjacking - javascript

I have this Javascript snippet in my application to prevent clickjacking:
<script language="javascript" type="text/javascript">
var style = document.createElement('style');
style.type = "text/css";
style.id = "antiClickjack";
style.innerHTML = "body{display:none !important;}";
document.head.appendChild(style);
if (self === top) {
var antiClickjack = document.getElementById("antiClickjack");
antiClickjack.parentNode.removeChild(antiClickjack);
} else {
top.location = self.location;
}
</script>
Basically, it creates a style element (CSS on the fly) to hide the body of the current page by default. Then, if it doesn't detect clickjacking, it deletes it. So, doing it this way, everyone who doesn't have Javascript can see the page too (although they won't be protected from clickjacking).
It works for every browser except for Internet Explorer, which throws a Unknown runtime error exception. Does someone have a suggestion on how to fix this?
Thanks :-)

You can't set the content of a <style> element via innerHTML. I think the correct property name is cssText but I'll have to check MSDN.
edit — yup that's it.
Thus your code can do this:
var style = document.createElement('style');
style.type = "text/css";
style.id = "antiClickjack";
if ('cssText' in style)
style.cssText = "body{display:none !important;}";
else
style.innerHTML = "body{display:none !important;}";

In the document HEAD element, add the following:
<style id="antiClickjack">body{display:none !important;}</style>
<script type="text/javascript">
if (self === top) {
var antiClickjack = document.getElementById("antiClickjack");
antiClickjack.parentNode.removeChild(antiClickjack);
} else {
top.location = self.location;
}
</script>

Related

Unknown Runtime Error when using JavaScript to append CSS to Body in IE8

I'm having an issue inserting a CSS #import rule into my <body> in Internet Explorer 8. I'm doing this as a way to defer non-critical CSS.
When I run the following code I get an "Unknown Runtime Error" on line 6 (ref_obj.innerHTML = ...):
var css_path_str = "my.css",
el_body = document.getElementsByTagName('body')[0],
ref_obj = document.createElement("style");
ref_obj.type = "text/css";
ref_obj.innerHTML = "#import url(\""+css_path_1_str+"\");";
el_body.appendChild(ref_obj);
As you can probably guess, the code works on Chrome and Firefox without any issue.
After doing a search here on SO, I stumbled on this post - Why is document.getElementById('tableId').innerHTML not working in IE8? - where it says that innerHTML for STYLE [and a few other elements] on IE is read-only.
Any ideas how I can edit my code to work around these limitations?
NOTE: Only pure Vanilla JavaScript.
EDIT:
Problem solved, but just for completeness, here is the code that should work (cross-browser).
var css_path_str = "my.css",
el_body = document.getElementsByTagName('body')[0],
ref_obj = document.createElement("style");
ref_obj.type = "text/css";
el_body.appendChild(ref_obj);
if(ref_obj.styleSheet)
{
//IE
ref_obj.styleSheet.cssText = "#import url(\""+css_path_str+"\");";
}
else
{
//Other Browsers
var ref_obj_text_node_obj = document.createTextNode("#import url(\""+css_path_str+"\");");
ref_obj.appendChild(ref_obj_text_node_obj);
}
What you can do to make IE happy is use the styleSheet.cssText property of the node after you've inserted it into the DOM:
var css_path_str = "my.css",
el_body = document.getElementsByTagName('body')[0],
ref_obj = document.createElement("style");
ref_obj.type = "text/css";
el_body.appendChild(ref_obj);
ref_obj.styleSheet.cssText = "#import url(\""+css_path_str+"\");";

How to use CSSStyleSheet.insertRule() properly?

I can't figure out where I'm going wrong here :/. When I run this code, all I get is a blank element. I can't seem to get the insertRule method to do anything (not even produce an error). Am I missing something?
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
</head>
<body>
<script>
var sheet = (function() {
// Create the <style> tag
var style = document.createElement("style");
// WebKit hack
style.appendChild(document.createTextNode(""));
// Add the <style> element to the page
document.head.appendChild(style);
return style.sheet;
})();
sheet.insertRule("\
#gridContainer {\
width: 100%;\
height: 100%;\
}\
", 0);
</script>
</body>
</html>
It is slightly confusing but your code does actually work, it is just that you can't see the inserted rules in the XML tree returned.
To verify that your code works, there are two tests you can do:
var style = (function() {
// Create the <style> tag
var style = document.createElement("style");
// WebKit hack
style.appendChild(document.createTextNode(""));
// Add the <style> element to the page
document.head.appendChild(style);
console.log(style.sheet.cssRules); // length is 0, and no rules
return style;
})();
style.sheet.insertRule('.foo{color:red;}', 0);
console.log(style.sheet.cssRules); // length is 1, rule added
<p class="foo">
I am some text
</p>
Run the above snippet, and you can see that the CSS rule does apply. And the cssRules property changes as well in the console.
This is often noted when browser extensions generate custom style-sheets appended to the DOM, and while debugging they appear as empty style-sheets in the inspector.
This version is based on Awal's answer and Totally Pwn CSS with Javascript from web archive.
The id parameter is useful for accesing the styleSheet with getElementById, and the media parameter is optinal and defauts to 'screen'.
I am returning the styleSheet.sheet, this is just my preference.
function createStyleSheet (id, media) {
var el = document.createElement('style');
// WebKit hack
el.appendChild(document.createTextNode(''));
el.type = 'text/css';
el.rel = 'stylesheet';
el.media = media || 'screen';
el.id = id;
document.head.appendChild(el);
return el.sheet;
}

How to determine when document has loaded after loading external css

How to determine when document has loaded(or is loading) after loading external css?
Normal page has loaded and complete at first time(with using document.onreadystatechange or document.readyStage), but after time script will call function to place a new stylesheet CSS into HTML for changing a background or images. During change stylesheet, document has still stage complete. Stage never has been changed after calling function? Why?
Timeline(example):
Visit one page : localhost/index.html
Document has stage loading
Document has stage complete
User was trying to change a theme, at this time stage hasnt been changed yet.
UPDATE: Without jQuery:)
UPDATE:
Example problem with using one image:
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<script>
document.onreadystatechange = function(){
console.log(document.readyState);
};
function checkDocumentState(){
console.log(document.readyState);
return setTimeout(function(){
checkDocumentState();
}, 1000);
}
checkDocumentState();
</script>
</head>
<body>
<img src="" onclick="this.setAttribute('src','http://i.imgur.com/uRBtadp.jpg')" style="width:50px; height:50px; background-color:gray; " /> Press empty image and open new image.
</body>
</html>
FOUND ANSWER: How can I tell when a CSS background image has loaded? Is an event fired?
But hopeless .. lack of universality...
CSS is called after DOM elements are populated. This is why in the days of dial up internet, the page would load all funky looking, and then all of a sudden start to develop into the desired page bit by bit. I would suggest using Jquery instead, where you could use the following code to be able to ensure the document is fully loaded and the CSS is already implemented
$document.ready(function() {
//Insert Code here
}
Hope that helps
Answering the question, how to determine the document has loaded after dynamically loading a css file depends upon the different browser vendors out there. There is not a single sure shot way for all the browsers, but lets tackle the problem one by one for each of these browsers.
Preface
var url = "path_to_some_stylesheet.css",
head = document.getElementsByTagName('head')[0];
link = document.createElement('link');
link.type = "text/css";
link.rel = "stylesheet"
link.href = url;
head.appendChild(link);
Once that appending is done:
Internet Explorer : fires readystatechange and load.
Opera : fires load event via onload.
Chrome : Doesnt fire an event but increments document.styesheets.length only after the file has arrived.
Firefox: I was not able to reliably get anything other than mozAfterPaint.
I wrote this code, what i wanted and worked for me:
window.engineLoading = {images_count:0, images_loaded_count:0, fonts_count:0, fonts_loaded_count:0 };
document.querySelector("a").onclick = function(){ // first elemnet a
var before_stylesheets_length = document.styleSheets.length;
var before_fonts_size = document.fonts.size;
document.fonts.onloadingerror = function(a){
window.engineLoading.fonts_loaded_count++;
}
document.fonts.onloading = function(a){
window.engineLoading.fonts_count++;
}
document.fonts.onloadingdone = function(a){
window.engineLoading.fonts_loaded_count++;
}
var head= document.getElementsByTagName('head')[0];
var style= document.createElement('link');
style.rel= 'stylesheet';
style.setAttribute("href","./new_style.css");
style.onload = function(){
for(i=before_stylesheets_length; i<document.styleSheets.length; i++){
var rules = document.styleSheets[i].rules;
for(q=0; q<rules.length; q++){
var styles = rules[q].style;
for(s=0; s<styles.length; s++){
console.log(styles[s]);
if((styles[s] == "background-image" || styles[s] == "background") && styles.backgroundImage.length > 0){
window.engineLoading.images_count++;
var body= document.getElementsByTagName('body')[0];
var image = document.createElement('img');
var url = styles.backgroundImage;
url = url.replace(/^url\(["']?/, '').replace(/["']?\)$/, '');
image.src = url;
image.width = 0;
image.height = 0;
image.setAttribute("class","pace-load-style");
image.onload = function(e){
console.log(e);
window.engineLoading.images_loaded_count++;
};
image.onerror = function(e){
window.engineLoading.images_laoded_count++;
}
body.appendChild(image);
break;
}
}
}
}
};
style.onerror = function(){};
head.appendChild(style);
setTimeout(function(){
checkCurrentState();
}, 1000);
return false;
};
function checkCurrentState(){
if(window.engineLoading.images_count == window.engineLoading.images_loaded_count && window.engineLoading.fonts_count == window.engineLoading.fonts_loaded_count){
console.log("loaded"); return true;
}console.log("still loading...");
return setTimeout(function(){
checkCurrentState();
}, 1000);
};
UPDATE: Scipt has bug on localfile because of empty rule. CSSRules is empty I don't worry about it , and no need fix it.
UPDATE: Mozilla Firefox hasnt reference document.fonts.

Set entire page's css on click?

Does anyone know how I can change the entire document's CSS file on click? I've searched around but only found a few results on setting a class/ID's CSS, not the entire document. My website has two themes, light/dark, and I want to load up "light.css" or "dark.css" from two links.
Thanks.
You need to change the src of the the link tag, which controls the styles. For example, you probably have this in your head tag:
<link rel="stylesheet" href="light.css">
You need to change the href attribute of the link tag to "dark.css" when you click something. You can do that like this:
document.getElementById('id-of-element').addEventListener('click',function(){
document.getElementsByTagName('link')[0].setAttribute('href',isDark?'light.css':'dark.css');
isDark=isDark?false:true;
}
IMPORTANT: you need to set isDark to false or true before this code, depending on whether the page is supposed to be dark or light in the beginning. You also need to change id-of-element to the id of the element that should be clicked to toggle the state of the page.
I think this is better than the other answers because it is simpler and uses no jquery.
EDIT: I accidentally had the src attribute instead of the href one before. I now updated it to be correct.
Yeah, you can do using theming. But the changing of CSS is limited to the <body> tag.
$("a.theme").click(function(){
$("body").addClass("dark");
});
I have used jQuery library to make the coding easier. And it is not a good idea to switch CSS rather, you can change the classes.
Demo
You can check out the working demo in jsBin.
Check out this answer for more details: Selecting a web page look and feel without reloading, with one CSS.
Try something like this:
Light
Dark
<script type="text/javascript" charset="utf-8">
$('a#light, a#dark').click(function(){
$('style').remove();
$.ajax({
url:'http://www.example.com/' + $this.attr('id') + '.css',
success:function(data){
$('<style></style>').appendTo('head').html(data);
}
})
})
</script>
Of course, you need to load jQuery first.
There's 2 ways that come immediately to mind.
1) Add a style tag to the page's head, ensuring that the style tag has a unique id. You can then set the innerHTML of that element. (somewhat messy)
2) Add a link tag to the page's head, also ensuring that it has a unique id. You set the type='text/css' and the rel='stylesheet' attributes. You the set the src of this link element to the appropriate css file.
Here's an example of each type. Just supply css files for theme3() and theme4() functions.
Example:
<!DOCTYPE html>
<html>
<head>
<script>
function byId(e){return document.getElementById(e);}
function newEl(tag){return document.createElement(tag);}
function newTxt(txt){return document.createTextNode(txt);}
function toggleClass(element, newStr)
{
index=element.className.indexOf(newStr);
if ( index == -1)
element.className += ' '+newStr;
else
{
if (index != 0)
newStr = ' '+newStr;
element.className = element.className.replace(newStr, '');
}
}
function forEachNode(nodeList, func)
{
var i, n = nodeList.length;
for (i=0; i<n; i++)
{
func(nodeList[i], i, nodeList);
}
}
window.addEventListener('load', mInit, false);
function mInit()
{
var style = newEl('style');
style.setAttribute('id', 'dynCss');
document.head.appendChild(style);
var style2 = newEl('link');
style2.setAttribute('type', 'text/css');
style2.setAttribute('rel', 'stylesheet');
style2.setAttribute('id', 'dynCss2');
document.head.appendChild(style2);
}
function theme1()
{
var style = byId('dynCss');
style.innerHTML = "h1{color: red;}";
var style2 = byId('dynCss2');
style2.setAttribute('href', '');
}
function theme2()
{
var style = byId('dynCss');
style.innerHTML = "h1{color: blue;}";
var style2 = byId('dynCss2');
style2.setAttribute('href', '');
}
function theme3()
{
var style = byId('dynCss');
style.innerHTML = "";
var style2 = byId('dynCss2');
style2.setAttribute('href', 'style3.css');
}
function theme4()
{
var style = byId('dynCss');
style.innerHTML = "";
var style2 = byId('dynCss2');
style2.setAttribute('href', 'style4.css');
}
</script>
<style>
</style>
</head>
<body>
<h1>This is the heading</h1>
<input type='button' onclick='theme1();' value='Theme 1'/>
<input type='button' onclick='theme2();' value='Theme 2'/>
<input type='button' onclick='theme3();' value='Theme 3'/>
<input type='button' onclick='theme4();' value='Theme 4'/>
</body>
</html>

Add stylesheet to Head using javascript in body

I'm working with a CMS that prevents us from editing the head section. I need to add css stylesheet to the site, right after the tag. Is there a way to do this with JS, where I can add a script to the bottom of the page (I have access to add script right before the tag) that would then inject the stylesheet into the head section?
Update: According to specs, the link element is not allowed in the body. However, most browsers will still render it just fine. So, to answer the questions in the comments - one really has to add link to the head of the page and not the body.
function addCss(fileName) {
var head = document.head;
var link = document.createElement("link");
link.type = "text/css";
link.rel = "stylesheet";
link.href = fileName;
head.appendChild(link);
}
addCss('{my-url}');
Or a little bit easier with jquery
function addCss(fileName) {
var link = $("<link />",{
rel: "stylesheet",
type: "text/css",
href: fileName
})
$('head').append(link);
}
addCss("{my-url}");
Original answer:
You don't need necessarily add it to the head, just add it to the end of body tag.
$('body').append('<link rel="stylesheet" type="text/css" href="{url}">')
as Juan Mendes mentioned, you can insert stylesheet to the head instead
$('head').append('<link rel="stylesheet" type="text/css" href="{url}">')
And the same without jQuery (see code above)
This will do what you want in an intelligent way. Also using pure JS.
function loadStyle(href, callback){
// avoid duplicates
for(var i = 0; i < document.styleSheets.length; i++){
if(document.styleSheets[i].href == href){
return;
}
}
var head = document.getElementsByTagName('head')[0];
var link = document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = href;
if (callback) { link.onload = function() { callback() } }
head.appendChild(link);
}
I've modified Eddie's function to remove or toggle the stylesheet on or off. It will also return the current state of the stylesheet. This is useful for example, if you want to have a toggle button on your website for vision-impaired users and need to save their preference in a cookie.
function toggleStylesheet( href, onoff ){
var existingNode=0 //get existing stylesheet node if it already exists:
for(var i = 0; i < document.styleSheets.length; i++){
if( document.styleSheets[i].href && document.styleSheets[i].href.indexOf(href)>-1 ) existingNode = document.styleSheets[i].ownerNode
}
if(onoff == undefined) onoff = !existingNode //toggle on or off if undefined
if(onoff){ //TURN ON:
if(existingNode) return onoff //already exists so cancel now
var link = document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = href;
document.getElementsByTagName('head')[0].appendChild(link);
}else{ //TURN OFF:
if(existingNode) existingNode.parentNode.removeChild(existingNode)
}
return onoff
}
Sample usage:
toggleStylesheet('myStyle.css') //toggle myStyle.css on or off
toggleStylesheet('myStyle.css',1) //add myStyle.css
toggleStylesheet('myStyle.css',0) //remove myStyle.css
You can use pure javascript and still elegance in the modern browser.
const range = document.createRange()
const frag = range.createContextualFragment(`THE CONTENT IS THE SAME AS THE HTML.`)
document.querySelector("YOUR-NODE").append(frag)
It's very easy to add any HTML code.
Document
createRange
createContextualFragment
Example 1
Add the style on the head by javascript.
<head></head><body><button class="hover-danger">Hello World</button></body>
<script>
const range = document.createRange()
const frag = range.createContextualFragment(`
<style>
.hover-danger:hover{
background-color: red;
font-weight: 900
}
</style>
`
)
document.querySelector("head").append(frag)
</script>
Example 2
Import CSS, JS, and modify the existing stylesheet.
<head></head>
<body><button class="btn btn-primary hover-danger">Hello world</button></body>
<script>
const range = document.createRange()
const frag = range.createContextualFragment(`
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/js/bootstrap.bundle.min.js"/>
`)
document.querySelector("head").append(frag)
window.onload = () => {
// 👇 If you don't want to import the new source, you can consider adding the data to exists source.
const nodeLink = document.querySelector(`link[href^="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/css/bootstrap.min.css"]`) // ^: match begin with my input
if (nodeLink) { // !== null
const stylesheet = nodeLink.sheet
const myCSS = `
background-color:red;
font-weight: 900;
`
stylesheet.insertRule(`.hover-danger:hover{ ${myCSS} }`, stylesheet.cssRules.length)
}
}
</script>
📙 You must have permission to modify the CSS directly
If you get the error:
Failed to read the 'cssRules' property from 'CSSStyleSheet': Cannot access rules,
then you can reference: https://stackoverflow.com/a/49994161/9935654
Here is a simple one-liner to add a stylesheet:
document.head.insertAdjacentHTML('beforeend', `<link typs="text/css" rel="stylesheet" href="<Source URL>">`);

Categories

Resources