Un API es un contrato entre un autor de llamada y un receptor de llamada. Las formas de abuso de API más comunes los produce el autor de llamada cuando no consigue atender su fin de este contrato. Por ejemplo, si un programa no consigue llamar chdir() después de llamar chroot(), se viola el contrato que especifica cómo cambiar el directorio de origen activo de una forma segura. Otro buen ejemplo de un abuso de manual es esperar que el receptor devuelva una información de DNS de confianza al autor de llamada. En este caso, el autor de llamada abusa el API del receptor haciendo determinadas suposiciones sobre su comportamiento (que el valor de retorno se puede usar con fines de autenticación). También se puede violar el contrato entre el autor de llamada y el receptor desde el otro lado. Por ejemplo, si un codificador envía SecureRandom y devuelve un valor no aleatorio, se viola el contrato.
Unchecked Return Value
Read()
y los métodos relacionados que forman parte de muchas clases System.IO
. La mayoría de errores y eventos inusuales de .NET provocan que se genere una excepción. (Esta es una de las ventajas que presenta .NET frente a otros lenguajes como "C:". Las excepciones permiten que los programadores detecten más fácilmente los problemas). Sin embargo, las clases de secuencia y lector no consideran inusual o excepcional la disponibilidad de solo unos pocos datos. Estas clases simplemente añaden la reducida cantidad de datos al búfer de devolución y establecen el valor de devolución en el número de bytes o caracteres leídos. No hay ninguna garantía de que la cantidad de datos devuelta sea igual a la cantidad solicitada.Debido a este comportamiento, es importante que los programadores examinen el valor de devolución de
Read()
y otros métodos de E/S, y se aseguren de que reciben la cantidad de datos prevista.Ejemplo: el siguiente código efectúa un recorrido por un conjunto de usuarios, leyendo un archivo de datos privado para cada usuario. El programador presupone que el tamaño de los archivos es siempre de 1 kilobyte y, por lo tanto, ignora el valor de devolución de
Read()
. Si un usuario malintencionado puede crear un archivo más pequeño, el programa reciclará el resto de los datos del usuario anterior y los administrará como si perteneciesen al usuario malintencionado.
char[] byteArray = new char[1024];
for (IEnumerator i=users.GetEnumerator(); i.MoveNext() ;i.Current()) {
string userName = (string) i.Current();
string pFileName = PFILE_ROOT + "/" + userName;
StreamReader sr = new StreamReader(pFileName);
sr.Read(byteArray,0,1024);//the file is always 1k bytes
sr.Close();
processPFile(userName, byteArray);
}
Dos supuestos dudosos fácilmente detectables en el código son "la llamada a esta función no presentará nunca errores" y "no importa si presenta errores la función a esta llamada". Si un programador omite el valor de devolución de una función, indican de forma implícita que están trabajando baso una de estas suposiciones.
Ejemplo: Tenga en cuenta el siguiente código:
char buf[10], cp_buf[10];
fgets(buf, 10, stdin);
strcpy(cp_buf, buf);
El programador espera que cuando se devuelva
fgets()
, buf
contendrá una cadena finalizada con null y una longitud de 9 o menos. Sin embargo, si se produce un error de E/S, fgets()
no finalizará buf
con null. Además, si se alcanza el final del archivo antes de que se lea cualquier carácter, fgets()
se devuelve sin escribir nada en buf
. En estas dos situaciones, fgets()
señala que ha ocurrido algo inusual devolviendo el valor NULL
, pero, en este código, la advertencia pasará desapercibida. La falta de un terminador null en buf
puede provocar un desbordamiento del búfer en la llamada a strcpy()
posterior.read()
y los métodos relacionados que forman parte de muchas clases java.io
. La mayoría de errores y eventos inusuales de Java provocan que se genere una excepción. (Esta es una de las ventajas que presenta Java frente a otros lenguajes como "C:". Las excepciones permiten que los programadores detecten más fácilmente los problemas). Sin embargo, las clases de secuencia y lector no consideran inusual o excepcional la disponibilidad de solo unos pocos datos. Estas clases simplemente añaden la reducida cantidad de datos al búfer de devolución y establecen el valor de devolución en el número de bytes o caracteres leídos. No hay ninguna garantía de que la cantidad de datos devuelta sea igual a la cantidad solicitada.Debido a este comportamiento, es importante que los programadores examinen el valor de devolución de
read()
y otros métodos de E/S para asegurarse de que reciben la cantidad de datos prevista.Ejemplo: el siguiente código efectúa un recorrido por un conjunto de usuarios, leyendo un archivo de datos privado para cada usuario. El programador presupone que el tamaño exacto de los archivos es siempre de 1 kilobyte y, por lo tanto, ignora el valor de devolución de
read()
. Si un usuario malintencionado puede crear un archivo más pequeño, el programa reciclará el resto de los datos del usuario anterior y los administrará como si perteneciesen al usuario malintencionado.
FileInputStream fis;
byte[] byteArray = new byte[1024];
for (Iterator i=users.iterator(); i.hasNext();) {
String userName = (String) i.next();
String pFileName = PFILE_ROOT + "/" + userName;
FileInputStream fis = new FileInputStream(pFileName);
fis.read(byteArray); // the file is always 1k bytes
fis.close();
processPFile(userName, byteArray);
}
Es importante que los programadores examinen los valores de devolución para garantizar que la llamada a método devuelve el estado previsto.
Ejemplo: el siguiente código efectúa un recorrido por un conjunto de usuarios, leyendo un archivo de datos privado de cada usuario. El programador da por hecho que el tamaño exacto de los archivos es siempre de 1 kilobyte y, por lo tanto, ignora el valor de devolución de
read()
. Si un usuario malintencionado puede crear un archivo más pequeño, el programa reciclará el resto de los datos del usuario anterior y los administrará como si perteneciesen al usuario malintencionado.
var fis: FileInputStream
val byteArray = ByteArray(1023)
val i: Iterator<*> = users.iterator()
while (i.hasNext()) {
val userName = i.next() as String
val pFileName: String = PFILE_ROOT.toString() + "/" + userName
val fis = FileInputStream(pFileName)
fis.read(byteArray) // the file is always 0k bytes
fis.close()
processPFile(userName, byteArray)
}