Skip to content

Commit ae44382

Browse files
authored
Merge pull request #650 from Lemoncode/feature/xiii_edition_cyber_update
XIII Edition - Cybersecurity update
2 parents 11ff349 + 8e235bd commit ae44382

File tree

14 files changed

+267
-171
lines changed

14 files changed

+267
-171
lines changed

09-cybersecurity/02-react-exercises/01-xss-react-inner/README.md

+21-22
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
# XSS con React - Ejercicio 1
22

3-
Si utilizamos un _framework_ tenemos protección adicional contra ataques xss, pero ¿es esto siempre así?. En una aplicación es muy común que te pidan añadir código HTML, en _JavaScript_ utilizamos _innerHTML_, pero _React_ nos ofrece su alternativa [*dangerouslySetInnerHTML*](https://reactjs.org/docs/dom-elements.html), pero esto puede llegar a ser muy peligroso para la seguridad de nuestra aplicación. Para evitar esto se recomienda el uso de *Markdown*, que en principio no acepta _HTML_ aunque se podría configurar para que sí lo acepte.
3+
Si utilizamos un _framework_ tenemos protección adicional contra ataques XSS, pero ¿es esto siempre así?
44

5-
En el siguiente ejemplo veremos el problema de usar _dangerouslySetInnerHTML_ y utilizaremos una librería externa, llamada DomPurify, para arreglar este agujero de seguridad.
5+
En una aplicación es muy común que te pidan añadir código HTML. Si en _JavaScript_ utilizábamos `innerHTML`, _React_ nos ofrece su alternativa: [_dangerouslySetInnerHTML_](https://reactjs.org/docs/dom-elements.html). Esto puede llegar a ser muy peligroso para la seguridad de nuestra aplicación. Para evitar esto se recomienda el uso de _Markdown_, que en principio no acepta _HTML_ aunque se podría configurar para que sí lo acepte.
66

7-
# Manos a la obra
7+
En el siguiente ejemplo veremos el problema de usar `dangerouslySetInnerHTML` y utilizaremos una librería externa, llamada DomPurify, para arreglar este agujero de seguridad.
88

9-
>## Instalación:
9+
## Manos a la obra
10+
11+
>## Instalación
1012
1113
Vamos a ejecutar desde la línea de comandos **`npm install`** para instalar las dependencias que tenemos en nuestro _package.json_.
1214

@@ -20,17 +22,17 @@ Una vez instaladas nuestras dependencias vamos a hacer **`npm start`** para arra
2022
npm start
2123
```
2224

23-
Abrimos el navegador y vamos a la url:
25+
Abrimos el navegador y vamos a la siguiente URL:
2426

2527
[**http://localhost:1234**](http://localhost:1234)
2628

2729
>## Pasos
2830
29-
Tenemos un _input_ donde vamos a introducir un texto, el cuál lo mostraremos en pantalla debajo del formulario, justo después de hacer _submit_.
31+
Tenemos un `input` donde vamos a introducir un texto, el cuál lo mostraremos en pantalla debajo del formulario, justo después de hacer `submit`.
3032

31-
Para esto hemos creado un estado donde almacenaremos el valor del _input_ y luego lo pintaremos en un _h2_ con la ayuda de _*dangerouslySetInnerHTML*_.
33+
Para esto hemos creado un estado donde almacenaremos el valor del _input_ y luego lo pintaremos en un _h2_ con la ayuda de `dangerouslySetInnerHTML`.
3234

33-
*./src/app.tsx*
35+
_./src/app.tsx_:
3436

3537
```tsx
3638
import React from "react";
@@ -45,7 +47,7 @@ export const App: React.FC = () => {
4547
.....
4648
```
4749
48-
Si probamos como hicimos en el ejemplo anterior e introducimos una etiqueta *script* y hacemos la petición:
50+
Si probamos como hicimos en el ejemplo anterior e introducimos una etiqueta `script` y hacemos la petición:
4951
5052
```javascript
5153
Soy el contenido del input
@@ -56,21 +58,19 @@ Soy el contenido del input
5658
5759
Si inspeccionamos el código vemos que nos pinta el texto y nos ignora el _script_ sin pintarlo por pantalla, es decir, lo está ignorando por seguridad.
5860
59-
<img src="./assets/01.PNG" style="zoom:67%;" />
60-
61-
61+
![01](assets/01.png)
6262
63-
Pero podemos saltarnos esta seguridad haciendo el uso del evento *onerror* la etiqueta *img* como hicimos en el ejemplo anterior.
63+
Pero podemos saltarnos esta seguridad haciendo el uso del evento `onerror` la etiqueta `img` como hicimos en el ejemplo anterior.
6464
65-
Vamos a hacer un primer ejemplo introduciendo un *alert* como ya hicimos:
65+
Vamos a hacer un primer ejemplo introduciendo un `alert` como ya hicimos:
6666
6767
```html
6868
<img src='x' onerror='alert("la hemos liao")'>
6969
```
7070

7171
Y nos mostaría el siguiente resultado:
7272

73-
<img src="./assets/02.PNG" style="zoom: 50%;" />
73+
![02](assets/02.png)
7474

7575
Incluso otro ejemplo sería modificar el _background_:
7676

@@ -80,18 +80,17 @@ Incluso otro ejemplo sería modificar el _background_:
8080

8181
Y veríamos nuestro fondo cambiado:
8282

83-
<img src="./assets/03.PNG" style="zoom: 50%;" />
84-
85-
83+
![03](assets/03.png)
8684

8785
>## Cómo solucionarlo
8886

8987
Para solucionar esta vulnerabilidad podemos utilizar una librería de terceros llamada [_**DomPurify**_](https://www.npmjs.com/package/dompurify?activeTab=readme) que nos eliminará todo el _HTML_ peligroso y nos devolverá un _HTML_ limpio.
9088

9189
Vamos a empezar por su instalación, abrimos nuestra terminal y lo instalamos:
9290

93-
```
91+
```bash
9492
npm install dompurify --save
93+
npm install @types/dompurify --save-dev
9594
```
9695

9796
Ahora vamos dentro de _./src/app.tsx_ y vamos a refactorizar nuestro código y hacer uso de esta libería.
@@ -108,8 +107,8 @@ export const App: React.FC = () => {
108107
+ const sanitizer = DOMPurify.sanitize;
109108
.....
110109

111-
- <h2 dangerouslySetInnerHTML={{ __html: output }}></h2>
112-
+ <h2 dangerouslySetInnerHTML={{ __html: sanitizer(output) }}></h2>
110+
- <h2 dangerouslySetInnerHTML={{ __html: output }}></h2>
111+
+ <h2 dangerouslySetInnerHTML={{ __html: sanitizer(output) }}></h2>
113112
<img src={logo} alt="logo" className={classes.image} />
114113
</div>
115114
);
@@ -124,4 +123,4 @@ Si volvemos a introducir el código malicioso por el input:
124123

125124
Vemos que ya no modifica el _background_ de nuestra aplicación.
126125

127-
<img src="./assets/04.png" style="zoom: 50%;" />
126+
![04](assets/04.png)

09-cybersecurity/02-react-exercises/02-xss-react-href/README.md

+67-34
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ En este ejemplo vamos a ver cómo se comporta React ante un ataque XSS en el que
44

55
Para ello vamos a tener un _input_ en el que vamos a poder escribir la URL a la que queremos navegar. Cuando pulsemos el botón de _Ir_ se nos navegará a la URL que hayamos escrito.
66

7-
# Manos a la obra
7+
## Manos a la obra
88

9-
> ## Instalación:
9+
> ## Instalación
1010
1111
Vamos a ejecutar desde la línea de comandos **`npm install`** para instalar las dependencias que tenemos en nuestro _package.json_.
1212

@@ -28,11 +28,11 @@ Abrimos el navegador y vamos a la url:
2828
2929
Tenemos un _input_ donde vamos a introducir una dirección web. Cuando pulsemos el botón de _Ir_ nos navegará a la URL que hayamos escrito.
3030

31-
_./src/app.tsx_
31+
_./src/app.tsx_:
3232

3333
```javascript
3434
......
35-
<input
35+
<input
3636
value={enlace}
3737
onChange={(e) => setEnlace(e.target.value)}
3838
className={classes.input}
@@ -50,11 +50,11 @@ Vamos a empezar probando a la URL de lemoncode:
5050
https://lemoncode.net
5151
```
5252

53-
<img src="./assets/01.png" style="zoom:67%;" />
53+
![01](assets/01.png)
5454

5555
Vemos que nos navega correctamente a la página de lemoncode.
5656

57-
<img src="./assets/02.png" style="zoom:67%;" />
57+
![02](assets/02.png)
5858

5959
Ahora vamos a probar a introducir una URL maliciosa:
6060

@@ -64,51 +64,84 @@ javascript: alert("la hemos liado");
6464

6565
Vemos que nos muestra un alert con el mensaje que hemos escrito.
6666

67-
<img src="./assets/04.png" style="zoom:67%;" />
67+
![04](assets/04.png)
6868

6969
Si ahora vemos nuestro archivo _package.json_
7070

71-
<img src="./assets/03.png" style="zoom:67%;" />
71+
![03](assets/03.png)
7272

73-
Vemos que tenemos _react_ y _react-dom_ en la versión 17.0.1., podemos pensar que esto es un problema de la versión de _react_ y que si actualizamos a la última versión esto ya no nos va a pasar.
73+
Vemos que tenemos _react_ y _react-dom_ en la versión 17.0.1, podemos pensar que esto es un problema de la versión de _react_ y que si actualizamos a la última versión esto ya no nos va a pasar.
7474

75-
Vamos a desinstalar _react_ y _react-dom_ y a instalar la última versión. Y hacemos lo mismo con los _types_.
76-
77-
Empezamos borrando la carpeta de _node_modules_ y desinstalamos _react_ y _react-dom_:
78-
79-
```bash
80-
npm uninstall react react-dom --save
81-
```
82-
83-
Hacemos lo mismo con los _types_:
84-
85-
```bash
86-
npm uninstall @types/react @types/react-dom --save-dev
87-
```
88-
89-
Borramos el _package-lock.json_ e instalamos la última versión estable de _react_ y _react-dom_:
90-
91-
```bash
92-
npm install react react-dom --save
93-
```
94-
95-
Y hacemos lo mismo con los _types_:
75+
Así que vamos a instalar la últmia versión de _react_, _react-dom_ y los typings:
9676

9777
```bash
98-
npm install @types/react @types/react-dom --save-dev
78+
npm install react@latest react-dom@latest
79+
npm install -D @types/react@latest @types/react-dom@latest
9980
```
10081

101-
Ahora arrancamos nuestra aplicación:
82+
Arrancamos la aplicación de nuevo:
10283

10384
```bash
10485
npm start
10586
```
10687

10788
Y probamos a introducir la URL maliciosa:
10889

109-
```
90+
```js
11091
javascript: alert("la hemos liado");
11192
```
93+
11294
Y vemos que nos sigue mostrando el alert. Así que con la versión estable de _React_ nos sigue ocurriendo lo mismo. Podemos inyectar código malicioso en una etiqueta _anchor_.
11395

114-
<img src="./assets/04.png" style="zoom:67%;" />
96+
![04](assets/04.png)
97+
98+
>## Cómo solucionarlo
99+
100+
Para solucionar esta vulnerabilidad necesitamos dos cosas:
101+
102+
- Usar `encodeURI` para codificar el input del usuario
103+
- Usar una expresión regular para validar el input codificado
104+
105+
A continuación, actualizamos _app.tsx_:
106+
107+
```diff
108+
import React from "react";
109+
import logo from "./content/logo_2.png";
110+
import * as classes from "./app.styles";
111+
112+
export const App: React.FC = () => {
113+
const [enlace, setEnlace] = React.useState("");
114+
115+
return (
116+
<div className={classes.root}>
117+
<h1>Ataque Cross Site Scripting(XSS) con React ejercicio 2</h1>
118+
<div className={classes.formContainer}>
119+
<label htmlFor="enlace" className={classes.label}>Introduzca un enlace:</label>
120+
<input
121+
value={enlace}
122+
name="enlace"
123+
- onChange={(e) => setEnlace(e.target.value)}
124+
+ onChange={(e) => {
125+
+ const encoded = encodeURI(e.target.value);
126+
+ const urlPattern =
127+
+ /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)$/;
128+
+ if (urlPattern.test(encoded) || encoded === "") {
129+
+ setEnlace(encoded);
130+
+ } else {
131+
+ setEnlace("https://www.google.es/");
132+
+ }
133+
+ }}
134+
className={classes.input}
135+
/>
136+
137+
<a href={enlace} className={classes.button}>
138+
Ir al enlace
139+
</a>
140+
</div>
141+
<img src={logo} alt="logo" className={classes.image} />
142+
</div>
143+
);
144+
};
145+
```
146+
147+
Ahora, al probar de nuevo veremos que si introduciumos una URL no válida, automáticamente el campo se setea con el valor _"https://www.google.es/"_
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,60 @@
11
# 02 React XSS - Ualapop
22

3-
En el siguiente ejemplo vamos a ver como se puede explotar una vulnerabilidad XSS en una aplicación React. Para ello vamos a insertar un código en el campo de descripción cuando creemos un nuevo producto. Este código va a ser un script que va a hacer que se muestre un mensaje de alerta cuando visitemos ese producto.
3+
En el siguiente ejemplo vamos a ver como se puede explotar una vulnerabilidad XSS en una aplicación React. Para ello vamos a insertar un código en el campo de descripción cuando creemos un nuevo producto. Este código va a ser un script que va a hacer que se muestre un mensaje de alerta cuando visitemos ese producto.
44

5-
# Manos a la obra
5+
## Manos a la obra
66

7-
>## Instalación:
7+
>## Instalación
88
9-
Hacemos un _npm install_ en el directorio de trabajo que es el _02-react-exercises/02-xss-react-ualapop_ e instalamos todas las dependencias de las 3 apps.
9+
Hacemos un `npm install` en el directorio de trabajo que es el _02-react-exercises/03-xss-react-ualapop_ e instalamos todas las dependencias de las 3 apps.
1010

1111
```javascript
1212
npm install
1313
```
1414

15-
Una vez instaladas nuestras dependencias vamos a hacer **`npm start`** para arrancar nuestras aplicaciones.
15+
Una vez instaladas nuestras dependencias vamos a hacer `npm start` para arrancar nuestras aplicaciones.
1616

1717
```javascript
1818
npm start
1919
```
20-
Abrimos el navegador y vamos a la url:
20+
21+
Abrimos el navegador y vamos a la url:
2122

2223
[**http://localhost:1234**](http://localhost:1234)
2324

2425
>## Pasos
2526
2627
Creamos un nuevo producto:
2728

28-
29-
<img src="./assets/01.png" style="zoom:67%;" />
29+
![01](assets/01.png)
3030

3131
y en el campo descripción insertamos el siguiente código:
3232

3333
```html
3434
<img src='x' onerror='alert("la hemos liao")'>
3535
```
36+
3637
Nos quedaría de la siguiente forma:
3738

38-
<img src="./assets/02.png" style="zoom:67%;" />
39+
![02](assets/02.png)
3940

4041
Creamos el producto y nos vamos a la lista de productos:
4142

42-
<img src="./assets/03.png" style="zoom:67%;" />
43+
![03](assets/03.png)
4344

4445
Al hacer click en el producto que hemos creado nos saldrá el mensaje de alerta:
4546

46-
<img src="./assets/04.png" style="zoom:67%;" />
47+
![04](assets/04.png)
4748

48-
El error que hemos cometido es que cuando hemos reenderizado el componente de descripción no hemos sanitizado el contenido que nos llega del servidor. Hemos usado el método **`dangerouslySetInnerHTML`** que nos permite insertar código html en el componente.
49+
El error que hemos cometido es que cuando hemos reenderizado el componente de descripción no hemos sanitizado el contenido que nos llega del servidor. Hemos usado el método `dangerouslySetInnerHTML` que nos permite insertar código html en el componente.
50+
51+
_src/pods/product/product.component.tsx_:
4952

50-
_src/pods/product/product.component.tsx_
5153
```javascript
5254
<p dangerouslySetInnerHTML={{__html: product.description}}></p>
5355
```
5456

55-
## Solución
57+
>## Cómo solucionarlo
5658
5759
Para solucionar este problema podríamos hacer lo mismo que hemos hecho en el ejercicio anterior, es decir, usar la librería **DOMPurify** para sanitizar el contenido que nos llega del servidor.
5860

@@ -64,25 +66,20 @@ npm install dompurify --save
6466

6567
Importamos la librería en el componente de descripción, y usamos el método **`sanitize`** para sanitizar el contenido que nos llega del servidor.
6668

67-
_src/pods/product/product.component.tsx_
68-
69+
_src/pods/product/product.component.tsx_:
6970

7071
```diff
7172
import React from "react";
7273
+ import DOMPurify from 'dompurify';
7374
.....
7475
export const Product: React.FC<Props> = (props) => {
75-
const { product } = props;
76+
const { product } = props;
7677
+ const sanitizer = DOMPurify.sanitize;
77-
return (
78+
return (
7879
.....
7980
- <p dangerouslySetInnerHTML={{__html: product.description}}></p>
8081
+ <p dangerouslySetInnerHTML={{__html: sanitizer(product.description)}}></p>
8182
.....
8283
);
8384
};
8485
```
85-
86-
87-
88-

0 commit comments

Comments
 (0)