New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Spring boot security #155
Spring boot security #155
Conversation
# Conflicts: # build.gradle
This implementation might be vulnerable to the bug described here: |
Yes, search for concurrency test in demo project |
Implementation is vulnerableI can confirm the same bug applies. package org.lognet.springboot.grpc.auth;
import static org.junit.Assert.assertThrows;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.lognet.springboot.grpc.GrpcServerTestBase;
import org.lognet.springboot.grpc.demo.DemoApp;
import org.lognet.springboot.grpc.security.AuthCallCredentials;
import org.lognet.springboot.grpc.security.AuthHeader;
import org.lognet.springboot.grpc.security.EnableGrpcSecurity;
import org.lognet.springboot.grpc.security.GrpcSecurity;
import org.lognet.springboot.grpc.security.GrpcSecurityConfigurerAdapter;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Import;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.test.context.junit4.SpringRunner;
import com.google.protobuf.Empty;
import io.grpc.Status;
import io.grpc.StatusRuntimeException;
import io.grpc.examples.SecuredGreeterGrpc;
import io.grpc.examples.GreeterGrpc.GreeterFutureStub;
import lombok.extern.slf4j.Slf4j;
@SpringBootTest(classes = DemoApp.class, properties = "spring.cloud.service-registry.auto-registration.enabled=false")
@RunWith(SpringRunner.class)
@Import({ ConcurrentAuthConfigTest.TestCfg.class })
@Slf4j
public class ConcurrentAuthConfigTest extends GrpcServerTestBase {
private AuthCallCredentials callCredentials = new AuthCallCredentials(
AuthHeader.builder().basic("test", "test".getBytes()));
@TestConfiguration
static class TestCfg {
@EnableGrpcSecurity
public class DemoGrpcSecurityConfig extends GrpcSecurityConfigurerAdapter {
@Override
public void configure(GrpcSecurity builder) throws Exception {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
List<GrantedAuthority> roles = Arrays.asList(new SimpleGrantedAuthority("ROLE_TEST"));
UserDetailsService users = new InMemoryUserDetailsManager(new User("test", "test", roles));
provider.setUserDetailsService(users);
provider.setPasswordEncoder(NoOpPasswordEncoder.getInstance());
builder.authenticationProvider(provider);
builder.authorizeRequests().anyMethod().authenticated();
}
}
}
@Test
public void concurrentTest() throws InterruptedException {
System.out.println();
final SecuredGreeterGrpc.SecuredGreeterBlockingStub unsecuredFutureStub = SecuredGreeterGrpc
.newBlockingStub(selectedChanel);
final SecuredGreeterGrpc.SecuredGreeterBlockingStub securedFutureStub = unsecuredFutureStub
.withCallCredentials(callCredentials);
int parallelTests = 10;
List<Thread> threads = new ArrayList<>();
// Number of threads that passed the test
AtomicInteger successCounter = new AtomicInteger(0);
AtomicInteger failureCounter = new AtomicInteger(0);
Runnable authenticated = () -> {
securedFutureStub.sayAuthHello(Empty.getDefaultInstance()).getMessage();
// log.warn("Call passed");
};
Runnable unauthenticated = () -> {
StatusRuntimeException err = assertThrows(StatusRuntimeException.class,
() -> unsecuredFutureStub.sayAuthHello(Empty.getDefaultInstance()).getMessage());
assertEquals(Status.Code.UNAUTHENTICATED, err.getStatus().getCode());
// log.warn("Call blocked");
};
// Check that the assertions work as is (single threaded)
authenticated.run();
unauthenticated.run();
for (int i = 0; i < parallelTests; i++) {
Thread success = new Thread(() -> {
for (int j = 0; j < 1000; j++) {
authenticated.run();
}
successCounter.incrementAndGet();
log.info("All passed");
});
success.setUncaughtExceptionHandler((thread, ex) -> {
log.error("SECURITY ???", ex);
});
threads.add(success);
Thread failure = new Thread(() -> {
for (int j = 0; j < 1000; j++) {
unauthenticated.run();
}
failureCounter.incrementAndGet();
log.info("All passed");
});
failure.setUncaughtExceptionHandler((thread, ex) -> {
log.error("SECURITY BYPASSED", ex);
});
threads.add(failure);
}
Collections.shuffle(threads);
for (Thread thread : threads) {
thread.start();
}
for (Thread thread : threads) {
thread.join();
}
assertAll(() -> assertEquals(parallelTests, successCounter.get()),
() -> assertEquals(parallelTests, failureCounter.get()));
}
@Override
protected GreeterFutureStub beforeGreeting(GreeterFutureStub stub) {
return stub.withCallCredentials(callCredentials);
}
} You will encounter the following errors:
java.lang.NullPointerException: null
at org.lognet.springboot.grpc.demo.SecuredGreeterService.sayAuthHello(SecuredGreeterService.java:22) ~[main/:na]
at io.grpc.examples.SecuredGreeterGrpc$MethodHandlers.invoke(SecuredGreeterGrpc.java:209) ~[main/:na]
at io.grpc.stub.ServerCalls$UnaryServerCallHandler$UnaryServerCallListener.onHalfClose(ServerCalls.java:172) ~[grpc-stub-1.29.0.jar:1.29.0]
at java.base/java.util.Optional.ifPresent(Optional.java:183) ~[na:na]
at org.lognet.springboot.grpc.security.SecurityInterceptor$SecurityServerCallListener.onHalfClose(SecurityInterceptor.java:43) ~[main/:na]
at io.grpc.internal.ServerCallImpl$ServerStreamListenerImpl.halfClosed(ServerCallImpl.java:331) ~[grpc-core-1.29.0.jar:1.29.0]
at io.grpc.internal.ServerImpl$JumpToApplicationThreadServerStreamListener$1HalfClosed.runInContext(ServerImpl.java:818) ~[grpc-core-1.29.0.jar:1.29.0]
at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37) ~[grpc-core-1.29.0.jar:1.29.0]
at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:123) ~[grpc-core-1.29.0.jar:1.29.0]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na] PS: I had a really hard time running the tests without docker/consul. EDIT: |
No description provided.