Skip to content

Instantly share code, notes, and snippets.

@GGrassiant
Last active September 1, 2020 22:07
Show Gist options
  • Save GGrassiant/7d37d9ef9c24051251ee22f528b955f8 to your computer and use it in GitHub Desktop.
Save GGrassiant/7d37d9ef9c24051251ee22f528b955f8 to your computer and use it in GitHub Desktop.
Debounce useEffect (with xss attack example_

Debouncing in React + XSS attack.md

  • In order to debounce but also avoid 2 request to the API, we need 2 pieces of state (term and denounced term)

  • Using dangerouslySetInnerHTML could lead to a xss attack

// libs
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const Search = () => {
const [term, setTerm] = useState('programming');
const [debouncedTerm, setDebouncedTerm] = useState(term);
const [results, setResults] = useState([]);
useEffect(() => {
const timerId = setTimeout(() => {
setDebouncedTerm(term);
}, 1000);
return () => {
clearTimeout(timerId);
};
}, [term]);
useEffect(() => {
const search = async () => {
const { data } = await axios.get('https://en.wikipedia.org/w/api.php', {
params: {
action: 'query',
list: 'search',
origin: '*',
format: 'json',
srsearch: debouncedTerm,
},
});
setResults(data.query.search);
};
search();
}, [debouncedTerm]);
const renderedResults = results.map((result) => {
return (
<div key={result.pageid} className="item">
<div className="right floated content">
<a
className="ui button"
href={`https://en.wikipedia.org?curid=${result.pageid}`}
>
Go
</a>
</div>
<div className="content">
<div className="header">{result.title}</div>
<span dangerouslySetInnerHTML={{ __html: result.snippet }}></span>
</div>
</div>
);
});
return (
<div>
<div className="ui form">
<div className="field">
<label>Enter Search Term</label>
<input
value={term}
onChange={(e) => setTerm(e.target.value)}
className="input"
/>
</div>
</div>
<div className="ui celled list">{renderedResults}</div>
</div>
);
};
export default Search;
const express = require('express');
const cors = require('cors');
const app = express();
app.use(express.static('public'));
app.use(cors());
app.get('/', (req, res) => {
if (req.query.srsearch === 't') {
res.send({
query: {
search: [
{
snippet: `
<img src="asdf" onerror="document.body.innerHTML = '<h1>HAHAHA, I control this app now!!!</h1>';"></img>
`,
},
],
},
});
} else {
res.send({
query: {
search: [],
},
});
}
});
app.listen(3001);
@GGrassiant
Copy link
Author

Note:

  • In order to debounce but also avoid 2 request to the API, we need 2 pieces of state (term and denounced term)

  • Using dangerouslySetInnerHTML could lead to a xss attack

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment