Uma classe Authenticator tem um método perform que recebe uma instância de Password e se o Authenticator julga que a senha contido no objeto está correto ele executa um trecho de código privilegiado (que no caso desse exemplo é um System.out.println() )
Criei duas versões desta classe, cada um está com problemas diferentes.
A classe Password é um "wrapper" simples para uma senha:
public class Password {
private char[] passwordChars;
public Password(String password) {
this.passwordChars = password.toCharArray();
}
public int length() {
return this.passwordChars.length;
}
public char getPasswordChar(int index) {
return this.passwordChars[index];
}
}
Este é o primeiro Authenticator, acho que é o mais fácil:
public final class Authenticator1 {
private Password adminPassword = new Password("aa!NN9sdhauwe32i");
public void perform(Password password) {
if (password == null) return;
boolean passwordCorrect = true;
try {
if (adminPassword.length() != password.length()) {
passwordCorrect = false; // pw length does not match
}
if (passwordCorrect) {
for (int i=0; i < adminPassword.length(); i++) {
if (adminPassword.getPasswordChar(i) != password.getPasswordChar(i)) {
passwordCorrect = false;
break;
}
}
}
} catch(Exception ex) {
ex.printStackTrace();
}
if (passwordCorrect) {
performPriviledged();
}
}
private void performPriviledged() {
System.out.println("Performing priviledged actions 1.");
}
}
E o segundo que talvez seja um pouco mais difícil:
public final class Authenticator2 {
private Password adminPassword = new Password("aa!NN9sdhauwe32i");
public void perform(Password password) {
if (password == null) return;
boolean passwordCorrect = true;
if (adminPassword.length() != password.length()) {
passwordCorrect = false; // pw length does not match
}
if (passwordCorrect) {
for (int i=0; i < password.length(); i++) {
if (adminPassword.getPasswordChar(i) != password.getPasswordChar(i)) {
passwordCorrect = false;
break;
}
}
}
if (passwordCorrect) {
performPriviledged();
}
}
private void performPriviledged() {
System.out.println("Performing priviledged actions 2.");
}
}
Então. O objetivo: Adicione tal código (de preferência dentro do método main desta classe Test) que consegue rodar o método "performPriviledged()" de uma (ou de ambas) dessas duas classes (Authenticator1 e Authenticator2).
public class Test {
private static final Authenticator1 authenticator1 = new Authenticator1();
private static final Authenticator2 authenticator2 = new Authenticator2();
public static void main(String[] args) {
//authenticator1.perform(new Password("aa!NN9sdhauwe32i"));
//authenticator2.perform(new Password("aa!NN9sdhauwe32i"));
}
}
Coloquei comentado como seria o jeito de invocar os métodos caso a gente soubesse a senha correta. Mas vamos pretender que nós não sabemos a senha. Este é um exemplo meio tosco, mas a intenção é somente demonstrar que escrever código seguro é uma tarefa difícil e quase sempre que possível é melhor utilizar os mecanismos de segurança existentes nos containers e frameworks do que escrever seu próprio mecanismo.
[quote=sapulha]Afff, tá parecendo questão do “topa tudo por dinheiro” !!!
[/quote]
Infelizmente não conheço Li uma descrição do show na wikipedia, mas não consigo ver o que tem de parecido. Você poderia explicar para um gringo ignorante?
public class MyPassword extends Password{
public MyPassword(){
super("");
}
public int length() {
return Integer.parseInt("");
}
}
public class Test {
private static final Authenticator1 authenticator1 = new Authenticator1();
private static final Authenticator2 authenticator2 = new Authenticator2();
public static void main(String[] args) throws ClassNotFoundException {
authenticator1.perform(new MyPassword());
}
}
[quote=Sami Koivu][quote=sapulha]Afff, tá parecendo questão do “topa tudo por dinheiro” !!!
[/quote]
Infelizmente não conheço Li uma descrição do show na wikipedia, mas não consigo ver o que tem de parecido. Você poderia explicar para um gringo ignorante?
[]s,
Sami[/quote]
se isso te conforta, eu também não entendi o comentário
import java.lang.reflect.*;
public class BreakTest {
private static final Authenticator1 authenticator1 = new Authenticator1();
private static final Authenticator2 authenticator2 = new Authenticator2();
public static void main(String[] args) throws Exception {
// Obviamente você não pode fazer isto...
//authenticator1.performPriviledged();
// mas pode fazer isto:
Method m = Authenticator1.class.getDeclaredMethod ("performPriviledged");
m.setAccessible (true);
m.invoke (authenticator1);
// Obviamente você não pode fazer isto...
//authenticator2.performPriviledged();
// mas pode fazer isto:
m = Authenticator2.class.getDeclaredMethod ("performPriviledged");
m.setAccessible (true);
m.invoke (authenticator2);
// ou mesmo isto:
Field f = Authenticator2.class.getDeclaredField ("adminPassword");
f.setAccessible (true);
Password p = (Password) f.get (authenticator2);
String senha;
StringBuffer sb = new StringBuffer();
for (int i = 0; i < p.length(); ++i) {
sb.append (p.getPasswordChar (i));
}
senha = sb.toString();
System.out.println ("Senha = " + senha );
authenticator2.perform (new Password (senha));
}
}
É uma resposta completamente correta, mas como eu tinha uma outra coisa em mente, por favor, deixem eu acrescentar mais uma regra. Vamos supor que o SecurityManager impossibilita o uso da reflection API.
Tem (pelo menos) mais um jeito de quebrar Authenticator2.
public class Test {
private static final Authenticator2 authenticator2 = new Authenticator2();
public static final int MIN_PASSWORD = 6;
public static final int MAX_PASSWORD = 20;
public static void main(String[] args) {
for (int i=Test.MIN_PASSWORD; i<Test.MAX_PASSWORD; i++ ){
authenticator2.perform(new MyPassword(new String(new char[i])));
}
}
}
public class MyPassword extends Password{
public MyPassword(){
super("");
}
public MyPassword(String str){
super(str);
}
private int var = 0;
public int length() {
return var++ == 0 ? super.length():0;
}
}
Sim, exato. Impressionante. Me diga, você achou difícil? Ou meio trivial?
A teste equivalente que eu fiz foi assim:
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 32; i++) {
authenticator2.perform(new Password(sb.toString()) {
boolean first = true;
public int length() {
if (first) {
first = false;
return super.length();
} else {
return 0;
}
}
});
sb.append(" ");
}
Mas a ideia é a mesma. Fazer o método length() retornar um valor "inconsistente". Na primeira vez retornar o tamanho da senha (que, pórem vai ter que ser adivinhado) e na segunda vez retornar 0 para que o laço for da comparação não executa nenhuma vez.
Achei um ótimo exercício. O primeiro, como você disse, é realmente mais simples, o segundo é mais difícil, e precisa de uma lógica mais complexa (levei um tempo considerável nesse último).
O importante é que através desse exemplo fica evidente que decisões de implementações de framework de segurança proprietários devem ser tratadas com MUITO cuidado.
Estou cansado de ver LOUCURAS como security.jar dentro de grandes empresas quais dizem proporcionar segurança máxima a seus clientes.
[quote=oandrade]Achei um ótimo exercício. O primeiro, como você disse, é realmente mais simples, o segundo é mais difícil, e precisa de uma lógica mais complexa (levei um tempo considerável nesse último).
O importante é que através desse exemplo fica evidente que decisões de implementações de framework de segurança proprietários devem ser tratadas com MUITO cuidado.
Estou cansado de ver LOUCURAS como security.jar dentro de grandes empresas quais dizem proporcionar segurança máxima a seus clientes.
Isso realmente me preocupa.
Abraço,
Osvaldo Andrade[/quote]
Concordo plenamente, Osvaldo.
Agora estou vendo que você já postou no fórum comentando sobre o assunto, sugerindo a utilização de JAAS em vez de o colega fazer ele mesmo. Legal.
Concordo. É um dos muitos problemas nesse(s) código(s).
Ontém reparei que mesmo corrigindo isto e o problema no Authenticator2 o usuário poderia ficar observando quantos vezes o método getPasswordChar() fica chamado e utilizar essa informação para quebrar a senha caráter por caráter.
Uma ideía bem legal. Só que acho que as pessoas que manjam da concorrência mais do que eu seriam mais indicados para realizar isto. Ou seja, o louds, etc.